SELECT * FROM left_table JOIN right_table 不会返回全部字段。SQL标准规定其仅展开当前作用域列,同名列会导致报错(PostgreSQL/SQL Server)或歧义(MySQL/SQLite),正确做法是用表别名显式列出所有字段并用AS处理重名。

SELECT * FROM left_table JOIN right_table 会返回全部字段吗
不会。SQL 标准里 SELECT * 在 JOIN 场景下只展开当前作用域的列,如果左右表有同名列(比如都叫 id),SELECT * 会报错(多数数据库如 PostgreSQL、SQL Server),或返回歧义列(MySQL 允许但结果不可靠)。
真正想“返回左右表全部字段”,本质是显式列出所有列,并处理重名问题。
- PostgreSQL / SQL Server:直接写
SELECT *+ JOIN 必报错,必须指定别名或逐个选列 - MySQL:允许执行,但若两表都有
id,结果中只保留一个id,且无法确定来自哪边 - SQLite:行为类似 MySQL,但更易在应用层出错(比如 ORM 映射失败)
正确写法:用表别名 + 显式列名 + AS 避免冲突
最稳妥的方式是给每张表设短别名,再把关心的字段全列出来,重名列用 AS 区分。
SELECT a.id AS user_id, a.name AS user_name, b.id AS order_id, b.amount, b.created_at FROM users a JOIN orders b ON a.id = b.user_id;
注意:
- 别名
a和b必须在FROM子句中定义,之后才能在SELECT中引用 - 不要写
SELECT a.*, b.*—— 多数引擎不支持这种语法(MySQL 5.7+ 允许但仍有重名风险) - 如果字段很多,可先用
PRAGMA table_info(table_name)(SQLite)或\d table_name(psql)查结构,再复制粘贴生成列清单
LEFT JOIN / RIGHT JOIN 下“全部字段”的含义更微妙
外连接时,“返回左右表全部字段”其实隐含了 NULL 填充逻辑:左表所有行都保留,右表匹配不到的字段补 NULL;反之亦然。但字段集合本身仍由 SELECT 明确决定,不是自动拼接。
-
LEFT JOIN后,右表字段可能为NULL,但列名和类型仍存在 - 如果右表有
NOT NULL字段,在LEFT JOIN结果里它照样可能是NULL—— 这是语义正确,不是 bug - 别试图用
COALESCE(b.id, 0)盲目替换,除非业务真需要默认值;否则掩盖了数据缺失事实
自动化生成全字段 SELECT 的实用技巧
手动敲几十个字段容易漏,可用元数据辅助生成(以 PostgreSQL 为例):
SELECT
string_agg('a.' || column_name || ' AS a_' || column_name, ', ') ||
', ' ||
string_agg('b.' || column_name || ' AS b_' || column_name, ', ')
FROM information_schema.columns
WHERE table_name IN ('users', 'orders')
AND column_name NOT IN ('created_at'); -- 排除不想选的列运行后复制结果,粘贴进主查询即可。注意:
- 不同数据库查列元数据的系统表名不同:
information_schema.columns(标准)、sqlite_master(SQLite)、sys.columns(SQL Server) - 生成的 SQL 要人工核对别名是否清晰,比如
a_created_at比a_created_at_1更易读 - 别依赖工具一次生成就完事——JOIN 条件变了、表结构升级了,这段生成逻辑就得同步更新
字段多、表多、别名乱,是 JOIN 查询里最容易让后续维护者抓狂的地方。宁可多花两分钟写清楚别名,也别省那几行代码换来三个月后自己看不懂的 a1 a2 b1。

















