Skip to content

MySQL SELECT 方法速查 ​

先确定“每行结果代表什么”,再选择查询方法:查明细用 WHERE,做统计用 GROUP BY,需要保留明细并计算排名用窗口函数。这里以 MySQL 8.4 为准,重点是 SELECT 的写法和结果含义。

按问题找方法 ​

想查询什么主要写法直接阅读
满足条件、含空值或重复值的数据WHERE、IS NULL、DISTINCT筛选与去重
每个客户的订单数、金额、已完成金额GROUP BY、HAVING、条件聚合分组统计
带用户名称的订单、没有订单的用户JOIN、LEFT JOIN、NOT EXISTS关联与存在性
高于平均值的数据、分步骤统计标量子查询、派生表、WITH子查询与 CTE
每个客户最新一笔、每组前 N、累计金额ROW_NUMBER、RANK、SUM OVER窗口函数
金额最高的前几笔、继续读取下一页ORDER BY、LIMIT、游标条件排序与分页
拼接文本、处理 NULL、按月显示COALESCE、CASE、日期与字符串函数查询语法与函数
统一样例数据与运行前提

先在独立练习库执行现有查询样例初始化。以下所有查询共用该数据,不需要为每个例子重新建表。

users(id, name, age, email):

idnameageemail
1Lin25lin@example.com
2Chen30NULL
3Wu22wu@example.com

orders(id, customer_id, amount, status, created_at):

idcustomer_idamountstatuscreated_at
1011800completed2024-01-01
1021500pending2024-01-02
1032200completed2024-02-01

下文是这份固定样例的预期结果。本次整理未连接 MySQL 执行 SQL;修改练习数据后结果会变化。示例无 ORDER BY 时,只说明结果内容,不承诺返回顺序。

筛选与去重 ​

需求SQL 示例预期结果 / 注意事项
选择需要的列SELECT id, name FROM users ORDER BY id;1/Lin、2/Chen、3/Wu;比 SELECT * 更明确
数值范围SELECT id FROM users WHERE age BETWEEN 23 AND 30 ORDER BY id;1、2;BETWEEN 包含两端
多条件组合SELECT id FROM orders WHERE status = 'completed' AND amount >= 500;101;AND/OR 混用时加括号
指定几个值SELECT id FROM orders WHERE status IN ('completed', 'pending') ORDER BY id;101、102、103
查缺失值SELECT id FROM users WHERE email IS NULL;2;不写 email = NULL
前缀匹配SELECT id FROM users WHERE name LIKE 'L%';1;大小写规则取决于排序规则
去重客户SELECT DISTINCT customer_id FROM orders ORDER BY customer_id;1、2;DISTINCT 对整个 SELECT 列组合去重
日期区间SELECT id FROM orders WHERE created_at >= '2024-01-01' AND created_at < '2024-02-01' ORDER BY id;101、102;半开区间也适用于带时间的字段

分组与条件统计 ​

需求SQL 示例预期结果 / 注意事项
总行数与非空数SELECT COUNT(*) AS total, COUNT(email) AS filled FROM users;total=3、filled=2
每个客户订单数SELECT customer_id, COUNT(*) AS n FROM orders GROUP BY customer_id ORDER BY customer_id;1/2、2/1
找到重复客户SELECT customer_id, COUNT(*) AS n FROM orders GROUP BY customer_id HAVING COUNT(*) > 1;1/2;可用于检查重复值,不代表订单重复
找累计消费超过 1000 的客户SELECT customer_id, SUM(amount) AS total FROM orders GROUP BY customer_id HAVING SUM(amount) > 1000;1/1300;WHERE 筛行,HAVING 筛分组
只统计已完成金额SELECT customer_id, SUM(CASE WHEN status = 'completed' THEN amount ELSE 0 END) AS paid FROM orders GROUP BY customer_id ORDER BY customer_id;1/800、2/200
按月统计订单SELECT DATE_FORMAT(created_at, '%Y-%m') AS month, COUNT(*) AS n FROM orders GROUP BY month ORDER BY month;2024-01/2、2024-02/1

只查询分组键或合法聚合表达式,不通过关闭 ONLY_FULL_GROUP_BY 来掩盖不明确的结果。SUM(DISTINCT amount) 去重的是金额,不是订单;同额的不同订单不应该因此少算。

多表关联与存在性 ​

需求SQL 示例预期结果 / 注意事项
订单附带用户姓名SELECT o.id, u.name FROM orders o JOIN users u ON u.id = o.customer_id ORDER BY o.id;101/Lin、102/Lin、103/Chen
所有用户及订单数SELECT u.id, COUNT(o.id) AS n FROM users u LEFT JOIN orders o ON o.customer_id = u.id GROUP BY u.id ORDER BY u.id;1/2、2/1、3/0;COUNT(*) 会把空侧行也计入
至少有一笔完成订单SELECT u.id FROM users u WHERE EXISTS (SELECT 1 FROM orders o WHERE o.customer_id = u.id AND o.status = 'completed') ORDER BY u.id;1、2;每个用户最多返回一行
没有任何订单SELECT u.id FROM users u WHERE NOT EXISTS (SELECT 1 FROM orders o WHERE o.customer_id = u.id);3;不受 NOT IN 子查询含 NULL 的陷阱影响

外连接先决定“是否保留未匹配用户”。例如筛右表的已完成订单时,将条件放在 ON 可保留没有完成订单的用户;放在 WHERE 则会过滤外连接补出的 NULL 行。

多个一对多表直接连接后,行数可能被乘大。统计每个用户的两个明细集合时,通常先分别聚合到用户粒度,再连接汇总结果。不要随手加 DISTINCT 或 SUM(DISTINCT amount) 作为通用修复。

排序、Top N 与分页 ​

需求SQL 示例预期结果 / 注意事项
全局金额最高两笔SELECT id, amount FROM orders ORDER BY amount DESC, id ASC LIMIT 2;101/800、102/500;唯一 ID 用于同分时稳定排序
每页两条的第二页SELECT id FROM orders ORDER BY id LIMIT 2 OFFSET 2;103;大偏移通常需要扫描并丢弃许多行
从上一页继续读SELECT id FROM orders WHERE id > 102 ORDER BY id LIMIT 2;103;102 应来自上一页最后一行
按时间和 ID 继续读SELECT id FROM orders WHERE created_at > '2024-01-02' OR (created_at = '2024-01-02' AND id > 102) ORDER BY created_at, id LIMIT 2;103;条件方向必须与排序方向一致

LIMIT 2 只取全局两行;“每个客户最新两笔”需要分组窗口排名。同样,先 MAX(created_at) 再任意取其他列,不能保证取到同一条订单。

组合查询的三个检查点 ​

  1. 行粒度:每行是订单、用户还是用户每月?连接后是否变多?
  2. 边界数据:没有订单、NULL、并列金额、同一天多笔记录是否符合预期?
  3. 访问路径:确认结果后再用 EXPLAIN 与索引 检查扫描和排序,不给所有 SELECT 套固定复杂度。

应用传入的条件值使用驱动参数绑定;表名、排序列不能当成普通值参数绑定,应通过允许列表选择。不要直接拼接用户输入。

继续阅读:查询练习、MySQL 8.4 SELECT、JOIN 语法、GROUP BY。

继续阅读