SELECT 是什么
SELECT 是 SQL 中唯一的读取语句,负责从表里取出行与列,本身不修改任何数据。书写顺序是 SELECT 列清单 FROM 表 WHERE 条件 GROUP BY 分组 HAVING 过滤 ORDER BY 排序 LIMIT 条数,但数据库实际按另一套逻辑顺序求值:先从 FROM 确定数据源,再过滤、分组、聚合,最后才计算 SELECT 表达式并排序截断。这条顺序解释了为什么 WHERE 里不能用 SELECT 定义的别名,而 ORDER BY 里可以。语法在 MySQL 8.0、PostgreSQL、SQL Server、Oracle 上基本一致,差异集中在函数名、分页写法与标识符引号规则。
基本信息
| 适用平台 | MySQL 8.0 / PostgreSQL 12+ / SQL Server / Oracle 等关系型数据库 |
|---|---|
| 语句类型 | DQL 数据查询,不改变数据,可用只读账号执行 |
| 语法骨架 | SELECT 列清单 FROM 表 [WHERE …] [GROUP BY …] [HAVING …] [ORDER BY …] [LIMIT n] |
| FROM 可否省略 | 可以,无表查询写作 SELECT 1 + 1,常用于探活与常量计算 |
| 权限要求 | 对相关表有 SELECT 权限;跨库跨 schema 查询需额外授权 |
典型使用场景
- 按主键或业务键取少量行:接口详情页、后台编辑回显
- 按条件筛选列表:管理后台的搜索、筛选、分页
- 统计聚合结果:报表里的计数、求和、分组汇总
- 给写入做预检:先 SELECT 确认影响范围,再执行 UPDATE 或 DELETE
- 导出与对账:与数据仓库或下游系统的结果集比对
- 线上排障:临时确认某条数据是否已按预期落库
基本示例
基本示例
SELECT id, name, city # 显式列出需要的列,不要依赖 SELECT *
FROM users # 数据来源表
WHERE status = 1 # 只保留有效用户
AND city IN ('Beijing','Shanghai') # 多值条件写成 IN 更简洁
ORDER BY created_at DESC # 按注册时间倒序
LIMIT 20; # 只看最近 20 条
SELECT COUNT(*) FROM users; # 只取一行统计值
SELECT 1 + 1; # 无 FROM 的常量查询,用于连接探活核心子句作用
| 子句 | 作用 | 求值时机 |
|---|---|---|
| FROM / JOIN | 确定数据来源与连接方式 | 最先,决定后续处理的输入行集 |
| WHERE | 过滤行,条件为 UNKNOWN 的行被丢弃 | 分组之前,不能引用聚合与列别名 |
| GROUP BY | 把行按列值合并成组 | WHERE 之后,之后只能引用分组列与聚合函数 |
| HAVING | 过滤分组后的结果 | 分组之后,条件里可以写聚合函数 |
| SELECT | 计算输出列,此刻才产生列别名 | 分组之后、排序之前 |
| ORDER BY | 对最终结果排序 | SELECT 之后,可用别名与列序号 |
| LIMIT / OFFSET | 截断返回行数与跳过偏移 | 最后一步执行 |
逻辑执行顺序
逻辑执行顺序
FROM -> WHERE -> GROUP BY -> HAVING -> SELECT -> ORDER BY -> LIMIT
# 记忆点:过滤行用 WHERE,过滤组用 HAVING
# 记忆点:别名在 WHERE 不可见,在 ORDER BY 可见两种分页写法
两种分页写法
-- 传统偏移分页:翻到后面越翻越慢
SELECT id FROM orders ORDER BY id LIMIT 20 OFFSET 4000;
-- 键集分页:用上次最后一条的 id 作起点,深度翻页代价稳定
SELECT id FROM orders WHERE id > 1000 ORDER BY id LIMIT 20;只写 SELECT 不写 ORDER BY 时,返回顺序由执行计划决定,同一语句两次执行顺序可能不同;分页、Top N、对账导出必须显式排序。
SELECT * 在开发期方便,但接口与导出建议显式列清单:加列、改列顺序都会影响按位置解析结果的程序。
大表上不加 WHERE 的查询会走全表扫描,在线上主库执行可能拖垮实例;先 EXPLAIN 看访问方式,或加 LIMIT 试跑。
MySQL 的 SELECT … FOR UPDATE / FOR SHARE 属于加锁读,只在显式事务里成对使用才有意义,否则锁在语句结束时立即释放。
常见误用对照
- 想过滤行却写成 HAVING:绕过了 WHERE 的索引能力,扫描量不变
- 想限流却指望 LIMIT 影响统计:LIMIT 只截断输出,聚合仍按全量算
- 把 ORDER BY 当成保证:不加 LIMIT 的排序在大结果集上代价高,且排序键重复时顺序仍不稳定
- 用 WHERE 过滤右表列却不自知:在外连接里这会退化成内连接
- 在 SELECT 里做业务计算再交给应用排序:口径分散在多处,后续改口径容易漏改