SQL基础回顾:SELECT、FROM、WHERE、GROUP BY、HAVING、ORDER BY

说起SQL,很多人觉得就是「查个数据嘛」。但在日志关联分析里,语句写得好不好,性能差个几十倍都很正常。今天我把最核心的六个子句串起来讲一遍——不仅讲语法,还讲我踩过的坑。

SELECT 与 FROM:你选了什么,从哪拿

SELECT 指定你要看的列,FROM 告诉数据库从哪张表取。逻辑上就是这么简单。

SELECT user_id, action, event_time
FROM user_actions;

但我在日志场景里经常遇到一种问题:有人直接写 SELECT *。日志表动辄几百列,全字段扫描把I/O吃满,查询慢得离谱。我个人习惯,哪怕只要三五个字段,也列出来。你看,这条语句在百万行日志上跑,时间能从8秒降到0.3秒。

小技巧:写 SELECT 时带上别名,尤其是多表关联时。比如 SELECT a.user_id, b.event_name,后面调试一眼就知道字段从哪来的。

WHERE:过滤出你要的那部分

WHERE 子句把不符合条件的行扔掉。日志分析里,90% 的性能优化靠 WHERE 写得对不对。

SELECT user_id, action, event_time
FROM user_actions
WHERE event_time >= '2024-01-01'
  AND event_time < '2024-02-01'
  AND action = 'login';

我曾经接手过一个慢查询,同事把时间过滤写成了 WHERE DATE(event_time) = '2024-01-15'。你知道问题在哪吗?DATE() 函数让索引失效了,全表扫描。后来改成范围条件,速度立马就上来了。

避坑:WHERE 里尽量别套函数在字段上。如果非得用函数,考虑建函数索引,或者换个写法绕过。

GROUP BY 与 HAVING:分组后算账

GROUP BY 把相同值的行合成一组,然后搭配聚合函数做统计。HAVING 呢,是对分组后的结果再做过滤——注意,不是对原始行,是对聚合后的组。

SELECT action, COUNT(*) AS cnt
FROM user_actions
WHERE event_time >= '2024-01-01'
GROUP BY action
HAVING cnt > 1000;

很多人把 WHERE 和 HAVING 搞混。我刚开始也犯过这个错:写了 WHERE COUNT(*) > 1000,数据库直接报错。为什么?因为聚合函数算的是组内的值,WHERE 在分组前执行,那时候组还没形成呢。

子句 执行时机 过滤对象 能否用聚合函数
WHERE 分组之前 原始行 不能
HAVING 分组之后 聚合后的组 可以
核心记忆点:WHERE 先筛行,GROUP BY 再分组,HAVING 最后筛组。顺序搞对了,逻辑就通了一半。

ORDER BY:把结果排整齐

ORDER BY 很简单,就是把返回的行按指定列排序。默认升序(ASC),要降序就写 DESC。

SELECT action, COUNT(*) AS cnt
FROM user_actions
WHERE event_time >= '2024-01-01'
GROUP BY action
ORDER BY cnt DESC;

嗯,这里要注意:ORDER BY 通常是查询里最耗资源的步骤之一,尤其是对大结果集做排序。我在线上遇到过一单,日志表三亿行,ORDER BY 直接让查询跑了五分钟。后来我改成先限定范围再排序——少了两个数量级的行,排序自然快了。

建议:如果不需要排序,就别写 ORDER BY。很多新手习惯性加个 ORDER BY id,其实完全没必要,反而增加负担。

六子句的执行顺序——这块面试常考

你写出来的 SQL 是「SELECT ... FROM ... WHERE ... GROUP BY ... HAVING ... ORDER BY」这个顺序,但数据库执行时完全是另一套顺序。有些人逻辑想不通,就是因为没搞明白执行顺序。

SQL 六子句执行顺序 ① FROM → 确定数据源 ② WHERE → 过滤行 ③ GROUP BY → 分组 ④ HAVING → 过滤组 ⑤ SELECT → 选列 ⑥ ORDER BY → 排序 注意 HAVING 必须放在 GROUP BY 之后, 不能出现在 WHERE 位置

你想想看,这个顺序是不是跟写作文一样?先确定从哪张表(FROM),再剔除不需要的行(WHERE),把剩下的行分组(GROUP BY),过滤掉不满足条件的组(HAVING),最后挑出你想要的列(SELECT)并排好序(ORDER BY)。

我记得有一次面试,对方让我用这个顺序解释一个慢查询。我按着图说了一遍,他当场就笑了:「你们培训过是吧?」其实不是培训,是踩坑踩出来的直觉。

一句话总结:WHERE 没法用聚合函数,HAVING 不能用原始列别名。记住执行顺序,你写 SQL 的时候脑子里就在「预编译」了。

好了,这六个子句是 SQL 的骨架。后面几章我们会在日志分析场景里反复用到它们。现在你可以随便找个日志表,把今天讲的每个子句都试一遍——尤其是 GROUP BY + HAVING 的组合,日志聚合统计里天天见。