SELECT 是 SQL 查询的核心操作,通过子句组合实现字段选取、过滤、聚合、连接、排序和分页;WHERE 筛行、HAVING 筛组;深分页宜用游标;相关子查询性能差;派生表须加别名。

SELECT 语句能做什么:不是“支持哪些操作”,而是“它本身就是查询操作的核心”
SELECT 不是容器式命令,不“支持”一堆操作;它是 SQL 查询的起点和主干。所有字段选取、过滤、聚合、连接、排序、分页,都通过它的子句(WHERE、GROUP BY、JOIN、ORDER BY、LIMIT 等)组合实现。单独写 SELECT * FROM t 是最简形式,加子句才构成完整查询逻辑。
-
SELECT后可跟字段名、表达式、函数(如COUNT()、CONCAT()、DATE_FORMAT()) - 表名可带别名(
FROM users u),字段也可用别名(SELECT name AS full_name) - 多表查必须显式声明连接方式(
INNER JOIN、LEFT JOIN),MySQL 不支持隐式逗号连接(除非明确需要笛卡尔积且理解风险)
WHERE 和 HAVING 的区别:一个筛行,一个筛组
WHERE 在分组前过滤原始数据行,作用于单行记录;HAVING 在 GROUP BY 之后过滤分组结果,常用于带聚合函数的条件(如 HAVING COUNT(*) > 5)。
-
WHERE中不能用聚合函数(WHERE SUM(amount) > 100报错) -
HAVING必须配合GROUP BY使用(除非整个查询无分组,此时HAVING退化为全局过滤,但语义模糊,不推荐) - 条件顺序影响性能:
WHERE越早排除无效行,后续计算越少;索引只能被WHERE利用,HAVING无法走索引
ORDER BY + LIMIT 组合分页:为什么 OFFSET 越大越慢
ORDER BY id ASC LIMIT 20 OFFSET 1000 看似简单,但 MySQL 仍需扫描前 1020 行才能跳过前 1000 行取后 20 行——OFFSET 不是“跳转指针”,而是“逐行计数”。
- 深分页场景应改用游标分页(
WHERE id > last_seen_id ORDER BY id LIMIT 20),依赖有序主键或唯一时间戳 -
ORDER BY字段必须有索引,否则排序会触发 filesort,内存/磁盘开销陡增 -
LIMIT若带两个参数(LIMIT 10,20),等价于LIMIT 20 OFFSET 10,含义相同,写法不同
SELECT 中的子查询:相关子查询容易拖垮性能
子查询可出现在 SELECT 列表(标量子查询)、FROM 子句(派生表)、WHERE 子句(IN / EXISTS)。其中相关子查询(即子查询引用外部表字段)每行执行一次,N 行就是 N 次查询。
- 避免在
SELECT列中写相关子查询(如SELECT id, (SELECT name FROM logs WHERE logs.user_id = users.id LIMIT 1) FROM users) -
EXISTS通常比IN更高效(尤其子查询结果可能含NULL时),因为找到第一条匹配就终止 - 派生表(
FROM (SELECT ...) AS t)必须带别名,否则报错Every derived table must have its own alias
MySQL 的 SELECT 灵活但不宽容:语法稍偏移就报错,逻辑稍复杂就变慢。真正难的不是记住支持什么,而是判断哪条路径在数据量、索引、事务隔离级别下实际可行。










