select *字段由表结构实时决定,每次查询动态读取数据字典;不推荐生产使用,尤其映射固定结构对象时易错位或panic;视图上更危险;explain format=tree可查实际展开字段。

SELECT * 返回的字段由表结构实时决定
执行 SELECT * 时,mysql 不会缓存字段列表,而是每次查询都从数据字典(INFORMATION_SCHEMA.columnS)中动态读取当前表的列定义。这意味着:如果在查询执行前有人用 ALTER table ... ADD COLUMN 增加了字段,SELECT * 就会包含它;反之,如果字段被删了,查询会直接报错 Unknown column。
- 不推荐在生产代码中使用
SELECT *,尤其当结果要映射到固定结构的对象(如 Go Struct、Python dataclass)时,字段增减会导致静默错位或 panic - 视图(VIEW)上的
SELECT *更危险——底层基表改了,视图定义不会自动更新,可能返回意外空值或类型不匹配 -
EXPLAIN FORMAT=TREE可查看优化器实际展开的字段列表,用于调试字段膨胀问题
AS 别名只影响结果集头部,不改变字段元信息
用 SELECT name AS full_name 这类写法,返回结果集中列名显示为 full_name,但该字段的原始类型、长度、是否允许 NULL 等元数据仍来自源列 name。客户端驱动(如 MySQL Connector/Python)通常通过 cursor.description 拿到的是别名,但类型信息还是底层字段的。
- 别名不能重复,否则 MySQL 报错
Column 'x' specified twice - 在
ORDER BY或HAVING中可以直接用别名,但WHERE和GROUP BY不行(执行顺序早于 SELECT),必须写原始表达式 - json 函数如
JSON_OBJECT('key', col)返回的是 JSON 类型字段,别名不影响其内容结构,只改外层键名
函数和表达式生成的字段没有原始列名,必须显式 AS
像 SELECT UPPER(name), count(*) 这样的语句,返回字段默认名称就是表达式本身(如 UPPER(name)、COUNT(*)),不同客户端处理方式不一:MySQL CLI 显示原样,某些 ORM 可能因无法解析而抛异常或返回空字符串。
- 所有计算字段都应加
AS,例如UPPER(name) AS upper_name,避免下游解析失败 -
COUNT(*)和COUNT(id)行为不同:COUNT(*)统计行数(含 NULL),COUNT(id)只统计id IS NOT NULL的行,字段选择直接影响语义 - 时间函数如
NOW()、CURDATE()返回常量字段,多次出现在同一 SELECT 中,每个都是独立求值(非复用),且无索引可利用
SELECT 字段顺序决定结果集列序,ORDER BY 不影响它
结果集的列物理顺序完全由 SELECT 子句中字段出现的先后决定,和后续的 ORDER BY、GROUP BY 无关。比如 SELECT b, a FROM t ORDER BY a,返回第一列永远是 b,第二列是 a,只是行的排列按 a 排了而已。
- 客户端绑定参数时(如 pdo::FETCH_NUM),下标 0 对应第一个 SELECT 字段,顺序错会导致数据塞进错误变量
- union 要求各子查询字段数、类型兼容性一致,顺序也必须严格对齐,否则报错
UNION types not match - 导出 CSV 时,字段顺序即文件列顺序,若业务依赖固定列位置(如财务系统模板),改 SELECT 顺序等于破坏契约
字段选择看着简单,但涉及执行计划、客户端兼容、语义一致性三层耦合。最易被忽略的是:表达式字段没加 AS 时,不同 MySQL 版本对默认别名的处理有差异(5.7 vs 8.0),升级后可能让旧应用突然解析失败。