首页/数据/数据库速查/SELECT 基础查询与列投影
SQL 语法52 条

SELECT 基础查询与列投影

SQL 查询的起点:指定列、给列起别名、用表达式与常量列;生产环境避免 SELECT * 以减少回表与网络开销。

数据库速查数据更新于 2026-10-13

SELECT 是什么

SELECT 是 SQL 的读取入口,把满足条件的行按指定的列投影出来。它的书写顺序是 SELECT、FROM、WHERE、GROUP BY、ORDER BY、LIMIT,而真实执行顺序是 FROM、WHERE、GROUP BY、HAVING、SELECT、ORDER BY、LIMIT。理解这个差异就明白为什么别名能在 ORDER BY 里用、在 WHERE 里却用不了。生产环境里列投影越窄越好:只取需要的列,覆盖索引才有机会生效,网络与客户端内存开销同步下降。

基本信息

用途读取数据与列投影
语法SELECT 列表 [FROM 表] [WHERE 条件] [GROUP BY 列] [HAVING 条件] [ORDER BY 列] [LIMIT n]
执行顺序FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY → LIMIT
别名规则ORDER BY 可引用别名,WHERE 不能
权限要求账号具备目标表的 SELECT 权限
版本差异MySQL 5.7 无 CTE,8.0 支持 CTE 与窗口函数

典型使用场景

  • 核对业务数据:只列需要的列,避免一次拉回整行宽表
  • 生成报表或导出:用表达式与别名把计算直接算好,减少后续处理
  • 巡检脚本取指标(行数、状态分布),配 -B 输出送监控采集
  • 分页列表接口的基础查询,配合排序与 LIMIT 使用
  • 排查数据异常时先 SELECT 确认影响范围,再决定写操作的条件
  • 用 LIMIT 抽样了解表结构与数据形态,避免全表扫描拖垮实例

常用示例

常用示例
SELECT id, sku, qty, price FROM orders;   # 明确列投影,替代 SELECT *
SELECT id AS order_id, qty * price AS amount FROM orders;   # 表达式计算并起别名
SELECT CONCAT('ORD-', id) AS order_no, created_at FROM orders   # 字符串拼接,加范围过滤
WHERE created_at >= '2026-10-01' AND created_at < '2026-10-02';
SELECT DISTINCT status FROM orders;   # 取去重枚举值,快速看清状态集合
SELECT COUNT(*) AS total FROM orders WHERE status='paid';   # 计数单独跑,别和明细混在一起
SELECT id, amount FROM orders WHERE user_id = 1001 LIMIT 10;   # 查明细务必带 LIMIT

执行顺序逐阶段读法

阶段做什么常见疑问
FROM确定数据来源,完成多表连接多表 JOIN 在这里完成,不是最后
WHERE行级过滤,作用在原始行上此处不能引用 SELECT 里的别名
GROUP BY按列分组,行数开始收敛分组列的选择直接决定结果粒度
HAVING对分组结果过滤可引用聚合函数,WHERE 不行
SELECT计算列与生成别名别名在这一步才真正产生
ORDER BY排序,可使用别名与列序号排序字段不唯一会导致分页错乱
LIMIT截断返回行数在排序之后生效,影响的是返回集

反例与改写

反例与改写
# 反例:函数包住索引列,索引无法使用
SELECT id, created_at FROM orders WHERE DATE(created_at) = '2026-10-12';
# 改写:改范围条件,索引恢复可用
SELECT id, created_at FROM orders
WHERE created_at >= '2026-10-12' AND created_at < '2026-10-13';
# 反例:只需要两列却拉回整行
SELECT * FROM orders WHERE user_id = 1001;
# 改写:只取需要的列,才有机会走覆盖索引
SELECT id, amount FROM orders WHERE user_id = 1001;
SELECT * 在宽表或含大字段(TEXT、BLOB)的表上会把不需要的数据全部读出来,覆盖索引失效,网络传输与客户端内存开销同时上升;只有在查看表结构或临时排查时才适合用。
只读查询一样可以造成影响:大表全表扫描会占满 buffer pool 与磁盘 IO,线上临时查询请自带 LIMIT、避开业务高峰,耗资源的统计尽量在只读从库执行。
MySQL 8.0 起支持 CTE 与窗口函数,复杂统计可以先用 WITH 定义中间结果集再关联;5.7 不具备这些能力,写需要跨版本复用的 SQL 时要先确认目标版本。

列投影与索引的配合

  • 查询涉及的列全部落在同一个索引上时可只读索引不回表,宽表场景收益最明显
  • 把不需要的大字段从投影里去掉,往往就直接消除了回表开销
  • 投影列与过滤条件尽量落在同一复合索引上,减少回表与临时排序
  • 统计类查询先确认是否需要精确值,估算场景可用统计信息替代全量计数
  • 列顺序与索引定义不一致时,最左前缀用不上的列会退化为扫描
列别名只在当前语句内有效、不能跨语句复用:需要长期统一的取数口径应落到视图或应用层,否则每个报表各写一遍,同一个指标会算出不同结果。

易错点

  • 在 WHERE 里引用 SELECT 定义的别名:别名在该阶段尚未生成,直接报未知列错误
  • 对索引列套函数或做算术(DATE()、LEFT()、列参与运算):索引失效退化为全表扫描,应改写为范围条件
  • 线上排查直接 SELECT * 且不带 LIMIT:宽表结果集撑爆客户端内存,同时把实例 IO 打满
  • 用 ORDER BY 加 LIMIT 分页时排序字段不唯一:翻页出现重复行或漏行,需补上唯一列作为次级排序
  • 条件值类型与列定义不一致导致隐式转换:字符串列与数字比较会逐行转换,既慢又可能得到意外结果
  • 把聚合列与明细列混在同一条 SELECT 里:开启 ONLY_FULL_GROUP_BY 时直接报错,未开启时结果也不确定,应拆成两条语句

数据来源与参考

  • MySQL 8.0 Reference Manual: SELECT Statement

相关工具

​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌