VALUES子句可直接作为内联表使用,无需SELECT *包裹;必须用括号包裹并显式声明列别名;类型由首行决定,需注意NULL顺序、数字精度及数据库兼容性差异。

VALUES 子句本身就能当表用,不需要再套 SELECT
很多人写 VALUES 时习惯加一层 SELECT * 包裹,比如:
SELECT * FROM (VALUES (1,'a'), (2,'b')) AS t(id, name)这多了一层子查询,不仅冗余,某些数据库(如 PostgreSQL 12+)还会略降优化器识别效率。直接把
VALUES 当作“内联表”参与 JOIN 更干净:FROM (VALUES (1,'a'), (2,'b')) AS t(id, name)注意括号不能省——
VALUES 必须用括号包裹才能在 FROM 中作为关系使用。
JOIN 多行常量表时,列名和类型对齐是关键
常量值的隐式类型由第一行决定,后续行必须兼容,否则报错(如 PostgreSQL 报 column "x" has type integer but expression has type text)。常见陷阱包括:
- 混用
NULL和字符串:第一行是'abc',第二行写NULL没问题;但若第一行是NULL,第二行写'abc',部分数据库会推导为unknown类型,导致 JOIN 失败 - 数字精度不一致:写
(1, 3.14), (2, 2.0),PostgreSQL 会统一为numeric,但 SQL Server 可能截断或报错 - 列别名必须显式声明:不写
AS t(id, name),多数数据库无法识别字段名,JOIN 条件里就用不了t.id
和真实表 JOIN 时,注意驱动表顺序与索引利用
VALUES 表本质是内存小表,通常应作为被驱动表(即放在 JOIN 右侧),让优化器优先走左边大表的索引。例如:
SELECT u.name, v.tag
FROM users u
JOIN (VALUES ('active', 1), ('pending', 2)) AS v(status_name, status_code)
ON u.status = v.status_code;这样 u.status 若有索引,就能高效定位;反过来写成 VALUES 左 JOIN users,可能触发全表扫描。另外,MySQL 8.0.19+ 支持 VALUES,但不支持在 ON 条件中下推过滤,所以尽量把过滤逻辑留在外层 WHERE。
不同数据库对 VALUES + JOIN 的支持差异要查清
不是所有数据库都一视同仁:
- PostgreSQL:完全支持,可嵌套、可带 CTE、可
ORDER BY内部 - SQL Server:支持,但
VALUES必须用括号,且不能直接跟ORDER BY(需套子查询) - MySQL:8.0.19+ 才支持,且不支持
VALUES后直接接AS别名(必须写成(VALUES ...) AS t) - SQLite:支持,但列类型推导较弱,建议显式转类型,如
CAST('a' AS TEXT)
跨数据库项目里,如果需要兼容老版本 MySQL 或 SQLite,VALUES 就得降级为临时表或 UNION ALL 模拟——这点容易被忽略,上线前务必在目标环境验证执行计划。










