<p>SELECT * 能查出所有物理定义列,但不包括计算列、隐藏列和临时生成列,且不跳过无权限列,解析阶段即报错;仅限调试或结构极稳定场景使用。</p>

SELECT * 会查出所有列,但不包括计算列和隐藏列
直接写 SELECT * 确实能返回表中所有物理定义的字段,但它不会包含视图里定义的计算列(除非该列显式建在基表上)、临时生成的列(如 ROW_NUMBER()),也不会返回系统隐藏列(如 SQL Server 的 %%physloc%% 或 PostgreSQL 的 ctid)。更关键的是,它**完全不感知权限**——如果某列你没 SELECT 权限,SELECT * 仍会报错,而不是跳过那列。
常见错误现象:ERROR: permission denied for column xxx,即使只读取自己有权限的列也会触发,因为优化器在解析阶段就校验全部列权限。
- 使用场景:仅适合开发调试、临时查数据、或表结构极稳定且无权限分片的内部工具
- 性能影响:在宽表(50+ 列)或带大字段(
TEXT、JSONB、BLOB)时,SELECT *显著拖慢网络传输和内存占用 - 兼容性注意:MySQL 8.0+ 在
SELECT *后接INTO OUTFILE时,字段顺序依赖建表顺序,而非SHOW COLUMNS输出顺序
用 INFORMATION_SCHEMA 查字段名比 SELECT * 更可靠
想“看到有哪些字段”,不该靠 SELECT * 返回结果反推,而应查元数据。不同数据库语法略有差异,但核心都是查 INFORMATION_SCHEMA.COLUMNS。
例如在 PostgreSQL 中执行:
SELECT column_name, data_type, is_nullable FROM information_schema.columns WHERE table_name = 'users' AND table_schema = 'public' ORDER BY ordinal_position;
MySQL 和 SQL Server 也支持类似查询,只是 table_schema 字段名可能叫 table_catalog 或需加 database() 函数。
- 为什么这样做:避免因视图、同义词、大小写敏感导致
SELECT *返回列名与预期不符 - 容易踩的坑:PostgreSQL 默认小写表名,但若建表时用了双引号(
"Users"),则table_name必须严格匹配大小写 - 参数差异:SQL Server 需查
sys.columns+sys.tables关联,INFORMATION_SCHEMA不返回IDENTITY或COMPUTED属性
SELECT * 在 JOIN 和子查询里容易引发歧义和错误
一旦涉及多表,SELECT * 就变成危险操作。两个表都有 id 和 created_at,结果集里就会出现重复列名,某些客户端(如 Python 的 psycopg2)甚至无法正确映射字段。
常见错误现象:psycopg2.ProgrammingError: column reference "id" is ambiguous,或 JDBC 报 SQLException: Column 'id' not found —— 实际是字段被覆盖了。
- 使用场景:禁止在
JOIN、UNION、CTE 或子查询中使用SELECT * - 替代方案:明确列出所需字段,并用别名消除歧义,例如
t1.id AS user_id, t2.id AS order_id - 性能影响:优化器对
SELECT *的 JOIN 很难做列裁剪(Column Pruning),可能导致不必要的磁盘 I/O 和排序开销
ORM 和迁移脚本里硬写 SELECT * 是隐性技术债
很多 ORM(如 Django ORM、SQLAlchemy Core)默认生成 SELECT *,看似方便,实则埋雷。当表结构变更(删列、改类型、加 NOT NULL 约束),应用可能突然崩溃,而且错误发生在运行时而非部署时。
例如 Django 的 Model.objects.all().values_list() 若未指定字段,底层仍是 SELECT *;SQLAlchemy 的 session.query(Model) 也一样。
- 实操建议:ORM 查询一律显式指定字段,如
.values('name', 'email')或query(Model.name, Model.email) - 迁移脚本中尤其要警惕:用
SELECT *导出数据再导入,若源库和目标库字段顺序/数量不一致,INSERT ... SELECT会静默错位 - 容易忽略的点:PostgreSQL 的
INSERT INTO t SELECT *要求目标表字段顺序、类型、数量完全一致,连默认值都不容错
字段顺序、权限边界、JOIN 上下文、ORM 执行路径——这些地方 SELECT * 看似省事,实际把问题从写代码阶段推迟到了运行时、上线后甚至数据修复环节。

















