news 2026/10/6 9:03:21

SQL每日一题:从去重到慢查询优化的实战复盘指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
SQL每日一题:从去重到慢查询优化的实战复盘指南

好的,遵照您的要求,我将仅依据提供的项目标题“sql每日一题”及相关关键词,撰写一篇符合所有规范的、直接可发布的Markdown格式博文。内容将完全围绕SQL学习与实操展开,不含任何违禁及敏感信息。


1. 为什么我坚持做“SQL每日一题”

干这行久了你会发现,SQL这东西,看十遍教程不如动手写一遍。尤其是面试前突击、换新数据库、或者接手老项目的时候,脑子里那点语法早就还给文档了。我给自己定了个规矩:每天至少解一道SQL题,不求难,但求稳。这个习惯坚持了快两年,收获远超预期。

所谓“SQL每日一题”,不是什么高深的方法论,就是每天拿出一道具体的SQL练习题,可能是去重、可能是窗口函数、也可能是慢查询优化场景,逼着自己用最快的速度写出最优解,然后复盘对比。它能解决的问题很实在:语法生疏、逻辑混乱、对数据库特性不了解、以及面试时手写SQL发怵。

这套内容适合谁?刚入门想打牢基础的新人,工作两三年想提升查询效率的开发者,以及准备跳槽需要系统性复习SQL的面试者。说白了,只要你的日常工作离不开数据库,这个习惯都值得养成。我接下来会把实操过程中总结的方法、踩过的坑、以及一些原理解析都掰开揉碎讲清楚。

2. 内容选题与思考路径拆解

2.1 每日一题怎么选:从高频场景反推

选题是第一步,也是最关键的一步。我的原则是“从高频场景反推”。什么意思?就是先去招聘网站、技术社区、以及自己平时的工作日志里收集那些反复出现的SQL问题,再按主题分类。

我平时会维护一个题单,大致分几个方向:基础查询(WHERE、JOIN、GROUP BY)、去重与排序(DISTINCT、ROW_NUMBER)、窗口函数(LAG、LEAD、SUM OVER)、子查询与CTE、性能优化(慢SQL、索引命中)、数据清洗(空值处理、重复数据剔除)。每天轮着来,保证覆盖面。

比如“SQL语句去重”这个场景,就特别值得单独练。很多新手一提去重就只会SELECT DISTINCT,但实际工作中,按月去重、按用户去重、按状态去重,逻辑完全不一样。DISTINCT只能去完全重复的行,而ROW_NUMBER()可以在分组内部去重,这才是高频需求。我一般会出一道类似“每个用户最近一笔订单”的题,强制自己用窗口函数而不是GROUP BY去写。

2.2 为什么坚持“小题大做”式复盘

一道题写出来并不算完,真正的价值在复盘。我每次做完题都会问自己三个问题:这个SQL能不能去掉一层子查询?能不能用更语义化的函数替代?如果数据量放大一千倍,这个写法还扛得住吗?

举一个真实的例子。有次我写了这样一条SQL:

select a.id, a.name from users a where a.created_at = (select max(created_at) from users where name = a.name)

功能没错,查每个名字下最近创建的用户。但复盘时发现,如果users表有几百万行,这个相关子查询会逐行执行,性能极差。改成窗口函数一行搞定:

select id, name from ( select id, name, row_number() over (partition by name order by created_at desc) as rn from users ) t where rn = 1

这就是“小题大做”的意义。题目本身不难,但通过对比不同写法,把性能差异和表达方式的优劣都暴露出来了。刷题不只是为了写对,是为了知道“在什么场景下用什么方案是更优的”。

2.3 从热搜词里挖考点:你踩过的坑别人也在踩

我会定期扫一遍搜索热词,看看大家最近都在查什么。热搜词往往能反映真实的痛点,比如“sql语句去重查询”“慢sql优化”“sql server writelog”“navicat for sql server激活码”,这些词背后都是具体的实操困境。

拿“慢SQL优化”来说,这是面试和工作中都绕不开的硬骨头。我复盘时专门总结过通用套路:先看执行计划,再看索引,再看SQL写法。具体来说,EXPLAIN输出里哪一行的type是ALL,就意味着全表扫描;key为NULL,就意味着没走索引。这些经验不通过大量“做题—踩坑—总结”的循环很难内化。

热搜词里还有一类是“sql server writelog”,这属于数据库日志膨胀问题,虽然不算标准SQL面试题,但工作中遇到会非常头疼。我把它也纳入每日一题的延伸学习,因为考试不考不代表实战不碰。

3. 核心SQL场景拆解与实操要点

3.1 去重场景:别只会DISTINCT

去重是“SQL每日一题”里出现频率最高的主题之一,也是新老手差距最明显的地方。

DISTINCT适合“完全重复行去重”。比如查所有不重复的部门名,一行搞定:

select distinct department_id from employees;

但如果是“按某字段分组取每组最新记录”,DISTINCT就无能为力了。需要靠窗口函数或自连接。

我常用的黄金套路是这样:

select * from ( select *, row_number() over (partition by user_id order by create_time desc) as rn from login_log ) t where rn = 1;

这段SQL的含义非常直观:先按user_id分组,在组内按create_time倒序编号,最后只保留每组第1行。我用这个方法处理过千万级日志表的去重,实测性能优于NOT EXISTS写法和GROUP BY后取MAX再回表查的写法。

再补充一个容易踩坑的点:在MySQL里,如果只查一个字段,比如“只统计不重复的用户数”,那直接SELECT COUNT(DISTINCT user_id)最高效。但如果你还想同时查这个用户的某条明细,DISTINCT就帮不上忙了。很多新人在这里卡半天,本质上是对“去重粒度”理解不到位。

3.2 空值处理:NULL比你想象的更阴险

SQL里最容易被忽视的坑就是NULL。NULL不等于0,不等于空字符串,更不等于FALSE。写“WHERE name != '张三'”时,那些name为NULL的行根本不会被查出来,因为NULL参与比较的结果是UNKNOWN,不是TRUE。

我每日一题里专门安排过几道空值处理题。最经典的一道:

select id, coalesce(score, 0) as score from exam_result;

COALESCE函数把NULL替换成0,这样后续做平均值、合计才不会把数据带偏。另一个常用的是IS NULL判断,比如查出从未登录过的用户:

select id from users where last_login_time is null;

注意这行SQL千万别写成“= NULL”,这是新手最常见的语法错误。

实际工作中,空值处理往往还涉及“净化数据源”的场景。有阵子在清洗一份订单表,发现大量电话号码字段是NULL,后来定位是上游接口漏传了字段。用SQL排查NULL分布范围的写法:

select count(*), sum(case when phone is null then 1 else 0 end) as null_cnt from orders;

通过这类题,你练的不只是函数,更是数据治理的边缘意识。

3.3 JOIN与子查询:谁先谁后有讲究

JOIN是SQL里概念最难讲清楚、用起来最容易出错的部分。我见过很多同事写LEFT JOIN时,因为过滤条件放错了位置,导致结果少了数据。

核心规则就一条:LEFT JOIN右边的表,如果要过滤,条件必须写在ON子句里,而不是WHERE里。举个例子:

select a.id, b.order_amount from users a left join orders b on a.id = b.user_id and b.status = 'paid';

如果把“status = 'paid'”移到WHERE里,那LEFT JOIN的结果会被过滤掉,相当于变成了INNER JOIN,很多没订单的用户就丢了。这个细节我至少在三个项目里帮别人排查过。

另外,能不用子查询就不用子查询。很多子查询可以改写成JOIN,性能会好一截。比如“查出每个分类销量最高的商品”,用窗口函数方案比用两层嵌套子查询简洁得多,这个我在2.2节的例子里已经复盘过。每日一题里反复练JOIN,就是为了让这些判断变成肌肉记忆。

4. 实操过程与核心环节实现

4.1 本地环境搭建:五分钟跑起来

搞SQL题,本地先有个能跑的环境很重要。我现在的配置是:MySQL 8.0 + Navicat,外加一台装着SQL Server 2019的虚拟机做兼容性验证。千万别只在在线刷题网站上写SQL,因为很多题要跑真实执行计划,本地环境更可控。

安装这块,我提几个容易踩的坑:

  • MySQL 8.0安装时如果选了“Use Strong Password Encryption”,老版本Navicat会连不上,建议换成“Use Legacy Password Encryption”。
  • SQL Server 2019安装失败,八成是权限或.NET环境问题,先装好.NET Framework 4.8再跑安装程序。
  • Navicat连不上SQL Server时,先去SQL Server配置管理器里启用TCP/IP协议。

一段最基础的建表语句,我每天练习都会用:

create table if not exists orders ( id int primary key auto_increment, user_id int not null, product_name varchar(50), amount decimal(10,2), status varchar(20), created_at datetime );

然后造一批测试数据,用存储过程循环插入一千行左右,够练习大部分题目了。真实项目里数据量更大,但刷题阶段用几百行数据验证逻辑对不对,性价比最高。

4.2 每日一题的完整SOP:从读题到复盘

我总结了一套固定执行流程,每天照着走,效率拉满:

  1. 读题:先把需求拆成年份、单位、过滤条件三个要素。比如“查2024年每月的销售总额”,年份是2024,单位是月,过滤条件是销售额。
  2. 写出第一版:想到什么写什么,保证正确性优先。
  3. 优化:检查能不能去掉子查询、能不能用窗口函数、能不能加索引。
  4. 跑EXPLAIN:看执行计划里有没有全表扫描。
  5. 复盘:把常用写法和“最优写法”记录到自己的题目库。

这套流程最大的好处是让练习有节奏感。每天只看一道题,知识点更聚焦;但偶尔也会遇到“这道题有多种解法”的情况,那我就把多种解法都跑一遍,记录各自的耗时,汇总成一张对比表。

4.3 索引调优与慢SQL实战:一道题压出性能差距

“SQL每日一题”如果只练语法,天花板很低。我每周会安排一到两道性能题,专门压执行计划。

比如这个案例:

select * from orders where status = 'paid' order by created_at desc limit 10;

几百行数据时毫无压力,但换到千万级表,这条SQL有可能走全表扫描。原因很简单:status区分度不高,成本优化器觉得走索引还不如扫全表。

优化办法是建立一个复合索引:

alter table orders add index idx_status_created (status, created_at);

有了这个索引,WHERE status和ORDER BY created_at都能命中索引,执行计划里的type会从ALL变成ref或range,性能立竿见影。

调优过程中,我强烈建议把执行计划读透。MySQL里EXPLAIN输出的关键字段就几个:type(访问类型)、key(命中的索引)、rows(预估扫描行数)、Extra(额外信息)。看到“Using filesort”就要警觉,说明排序没走索引;看到“Using temporary”说明有临时表,大查询里很危险。

4.4 SQL Server专项:从安装到日志处理的完整备忘

热搜词里不少是关于SQL Server的,2022企业版密钥、writelog日志、安装教程这些,都是实战型问题。作为每日一题的一部分,我也会用SQL Server做兼容性验证,因为T-SQL和MySQL语法存在差异,比如TOP与LIMIT、GETDATE与NOW()。

SQL Server 2019/2022安装时,比较容易在“SQL Server配置管理器”里卡住。如果安装后服务起不来,先去Windows事件查看器看错误日志,大概率是服务账号权限或端口冲突。安装完成后,记得在“SQL Server网络配置”里把TCP/IP协议启用,否则外网工具连不上。

再提一个“writelog”问题。SQL Server的日志文件如果不断膨胀,多半是因为数据库处于“完整恢复模式”且没有定期备份日志。解决思路是:

alter database 你的库名 set recovery simple;

切到简单模式后,日志不再无限增长。但这会牺牲时间点恢复能力,生产库慎用。刷题阶段无所谓,但要知道这个操作的含义。

我个人的建议是,本地练习环境就装SQL Server Express版,免费且够用,配合Navicat或SSMS都很顺手。密钥问题在个人练习场景其实不需要纠结,Express版游客登录就好。

5. 常见问题与排查技巧实录

5.1 执行计划看不懂?照着这几个字段先扫一眼

很多人拿到EXPLAIN输出就发懵,字段一个也看不明白。我提供一个极简排查顺序:

字段重点看什么危险信号
typeconst/ref/range好,ALL坏type=ALL即全表扫描
key命中的索引名key为NULL说明没走索引
rows预估扫描行数rows远大于预期需要警惕
ExtraUsing index好,Using filesort/temporary坏出现filesort要优化排序

只要这几项扫一遍,大部分慢查询的死因都能锁定。再看热搜词里“ora-12518”这类Oracle监听错误,其实也属于排查问题,思路是查监听状态、看端口通不通、确认服务是否注册成功,跟MySQL排查思路大同小异。

5.2 递归查询、函数报错与注入防范:三道让新手破防的题

LAG、LEAD这类窗口函数,考试常考,但工作中很多人不敢用。我前两天刚复盘过一道求“同比环比”的题:

select month, amount, lag(amount, 1) over (order by month) as prev_amount from monthly_sales;

这段SQL直接取出前一个月的销售额,比自连接简单太多。窗口函数最怕的是乱用PARTITION BY,我在分析用户行为数据时踩过坑:PARTITION BY和ORDER BY的顺序、组合一旦搞错,结果直接对不上。

还有一类题专门考察“函数副作用”。比如SQL Server里用MD5加密,T-SQL写法是:

select HASHBYTES('MD5', '123456');

这个函数返回的是VARBINARY,直接输出是一串不可读的二进制。很多人以为加密后应该是一串十六进制字符串,拿到结果就先懵了。解决办法是包一层CONVERT转成VARCHAR或打印十六进制:

select CONVERT(varchar(32), HASHBYTES('MD5','123456'), 2);

至于SQL注入防护,刷题阶段就要建立正确认知:永远不要用拼接字符串搭SQL,永远走参数化查询。面试时极大概率会问到“万能密码绕过”,你只要回答“用参数化查询,避免拼接,收窄数据库账号权限”,就已经答到点子上了。这个习惯不能光背,得写进每天的SQL练习里。

5.3 常用工具与“激活码”陷阱:正版意识要从练习期养成

热搜词里有一类我不推荐碰的,“navicat for sql server激活码”。这类工具的高级特性,官方试用版基本都能满足日常练习,没必要冒着安全风险去找破解资源。作为从业者,版权意识也算基本功之一。

工具选型上,我推荐一套组合:

  • MySQL环境:Navicat或DBeaver,DBeaver社区版免费。
  • SQL Server环境:官方SSMS,体验可以,而且完全免费。
  • 在线刷题:SQLZoo、LeetCode数据库题库,随时开刷。

工具只要能跑SQL、看执行计划、看表结构,就够用了。纠结于“哪款工具的皮肤好看”,纯属浪费时间。我试过从Toad切到DBeaver,又换回Navicat,最后还是看功能需求来决定。每日一题的效率,从来不在工具,在思路。

6. 把“SQL每日一题”沉淀成自己的题库

坚持了这么长时间,最大的心得是:刷题不是目的,积累成自己的知识库才是。我会给每道题打标签,比如“窗口函数”“去重”“索引优化”,再用Markdown表格记录题目描述、我的第一版答案、优化后答案、踩坑点。隔一个月回头翻,比收藏一堆教程管用得多。

比如我题库里有一条记录是这样的:

标签题目初版写法优化写法踩坑点
窗口函数查每个用户最近登录时间GROUP BY + MAX + 回表ROW_NUMBER() OVER(PARTITION BY)GROUP BY后无法取整行,回表代价高

这种结构化复盘,能让你刷一题精一题,而不是刷一百题忘一百题。另外,我会把同主题的题串成一条线,比如先练DISTINCT去重,再练GROUP BY去重,再练ROW_NUMBER去重,最后练性能对比。同一个业务需求,用不同SQL实现,理解深度完全不一样。

最后再分享一个小技巧:每周挑一天,不打开编辑器,纯手写SQL。模拟面试场景,限定五分钟写出一条“查最近30天内下单超过三次的用户”的SQL。写完之后先不看资料,再想两个问题:这个SQL的索引命中情况如何?如果改窗口函数会不会更好?这个过程比无脑刷一百道题有用得多。我试过之后,面试现场手写SQL时的肌肉记忆,都是这么练出来的。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/6 9:02:58

video-scroll:滚动即播停滚即停的轻量实现与避坑指南

简介:这份资源是一套用于滚动开始与停止视频播放的轻量级 JavaScript 实现,面向需要为网页添加滚动触发视频控制逻辑的前端开发者,尤其适合刚接触 jQuery 与 DOM 事件、希望快速上手交互效果的入门与中级学习者。压缩包共 3 个文件&#xff0…

作者头像 李华
网站建设 2026/10/6 9:02:29

Anolis 8 静默安装 Oracle 11g 实战:依赖、内核参数与避坑指南

简介:这份资源面向需要在龙蜥Anolis操作系统上部署Oracle 11g数据库的运维与DBA人员,提供了一套可直接落地的安装与恢复方案。Anolis OS作为阿里云维护的企业级Linux发行版,是运行Oracle数据库的稳定基础环境,而该包通过自动化脚本…

作者头像 李华
网站建设 2026/10/6 9:01:54

YashanDB性能评估指南:6大核心指标与压测方法

YashanDB最近在国产基础软件圈子里存在感不低,厂商宣发材料里常出现“性能比肩国际主流数据库”这种话。但数据库选型这件事,光看PPT和跑分广告没有用,任何库到了手上都要先搭压测环境、把核心性能指标跑一遍,再决定能不能上生产。…

作者头像 李华
网站建设 2026/10/6 9:01:01

阿拉伯文HTML/CSS模板实战:RTL布局从入门到避坑

简介:这是一份面向阿拉伯语网站开发场景的 HTML 与 CSS 基础模板,适合需要快速搭建 RTL(从右到左)排版页面的前端初学者与开发者使用。模板在布局与样式上兼顾阿拉伯文的书写方向、文本对齐及文化审美习惯,可帮助不熟悉…

作者头像 李华
网站建设 2026/10/6 9:00:55

Maven实战指南:从安装配置到依赖管理与问题排查

1. Maven到底是干嘛的,为什么Java开发绕不开它先聊一个很多人刚入行时都会问的问题:Maven是干嘛的?网上搜出来的解释十有八九是"项目管理和构建自动化工具",听起来很正式,但对新手来说等于没讲。我换个说法&…

作者头像 李华
网站建设 2026/10/6 9:00:20

深入理解AHB总线:从架构原理到工程实践

1. AHB总线到底是个什么东西做SoC设计或者嵌入式底层开发的朋友,对AHB这三个字母应该都不陌生。AHB全称是Advanced High-performance Bus,属于ARM AMBA总线家族里的高性能成员,专门负责芯片内部高速数据的搬运工作。简单理解,它就…

作者头像 李华