VALUES 列表
VALUES 可以直接构造一个临时行集,不必先创建表:
VALUES
(1, '待支付'),
(2, '已支付'),
(3, '已取消');
每对圆括号表示一行,行内各表达式依次形成列。这个结果可以像查询结果一样被读取,也可以作为 INSERT 的数据来源。
VALUES 很适合表达少量、明确、只在当前语句中使用的数据。大量或长期维护的业务数据仍应存入有约束、索引和权限控制的正式表。
基本结构
语法可以概括为:
VALUES
(expression_1, expression_2),
(expression_1, expression_2),
(expression_1, expression_2);
所有行必须具有相同的列数:
-- 合法:每行都是两列
VALUES
(1, 'A'),
(2, 'B');
-- 错误:第二行少一列
VALUES
(1, 'A'),
(2);
每个位置可以是字面量、参数或其他允许的值表达式:
VALUES
(:product_id, :quantity, CURRENT_TIMESTAMP),
(:another_product_id, :another_quantity, CURRENT_TIMESTAMP);
函数、标量子查询和复杂表达式能否出现在特定 VALUES 上下文中,可能受数据库限制。表达式也不应依赖某种从左到右的求值顺序。
为行集和列命名
独立 VALUES 结果的默认列名因数据库而异,可能类似 column1、column2。需要继续引用时,应显式设置表别名和列名:
SELECT s.status_code, s.status_name
FROM (
VALUES
(1, '待支付'),
(2, '已支付'),
(3, '已取消')
) AS s(status_code, status_name)
ORDER BY s.status_code;
这里:
s是临时行集的表别名;status_code和status_name是两列的名称;- 外层查询可以像读取普通表一样筛选、连接和排序。
不同数据库对派生 VALUES 的括号和别名语法略有差异。例如某些 MySQL 版本的表值构造器语法与 PostgreSQL、SQL Server 不完全相同,应核对目标版本。
列类型如何确定
数据库会把每个列位置上的所有表达式合并为一个公共类型:
VALUES
(1, 'basic'),
(2, 'premium');
第一列推断为整数类,第二列推断为字符类。若同一位置的类型不兼容,查询会报错或发生不希望的隐式转换:
VALUES
(1),
('not-a-number');
不同数据库可能把它们统一为文本,也可能尝试把文本转换为数字并失败。需要稳定类型时,应显式 CAST:
VALUES
(CAST(1 AS DECIMAL(12, 2))),
(CAST(2.50 AS DECIMAL(12, 2)));
NULL 需要类型上下文
NULL 本身没有足够具体的业务类型。其他行通常能提供推断上下文:
VALUES
(1, 'Alice'),
(2, NULL);
如果整列都是 NULL,或者结果要传给类型要求严格的运算,应显式转换:
VALUES
(1, CAST(NULL AS VARCHAR(100))),
(2, CAST(NULL AS VARCHAR(100)));
这可以避免数据库选择未知类型、默认文本类型或在后续连接时发生转换问题。
把 VALUES 用于 INSERT
VALUES 最常见的用途是一次插入一行或多行:
INSERT INTO products (sku, product_name, price)
VALUES
('BK-001', 'SQL 入门', 59.90),
('BK-002', '数据库设计', 79.00);
目标列与每个值列表按位置对应。应始终显式写出目标列名,以免表结构变化后产生错位。
在 INSERT 中,部分数据库允许使用 DEFAULT 让某个位置采用列默认值:
INSERT INTO products (sku, product_name, price, stock)
VALUES
('BK-003', '事务基础', 66.00, DEFAULT),
('BK-004', '索引实践', 72.00, 10);
DEFAULT 在这里不是普通值表达式。它通常只能出现在数据库明确允许的写入上下文中,不能假定可以放入独立 VALUES 查询、函数参数或算术表达式。
所有表约束依然生效,包括数据类型、NOT NULL、唯一键、外键和 CHECK。一条多行插入中某行失败时,整条语句通常具有原子性,但事务状态和错误处理仍应按目标数据库确认。
把 VALUES 作为临时映射表
少量固定映射可以直接参与连接:
SELECT o.order_id, o.status, m.status_name
FROM orders AS o
JOIN (
VALUES
('pending', '待支付'),
('paid', '已支付'),
('cancelled', '已取消')
) AS m(status_code, status_name)
ON m.status_code = o.status;
这比很长的 CASE 更像一张映射表,也方便同时携带多个属性:
VALUES
('pending', '待支付', 1),
('paid', '已支付', 2),
('cancelled', '已取消', 3)
但若映射被多个查询复用、需要外键约束、需要由业务人员维护,或会持续增长,就应建立正式字典表,而不是在各处复制 VALUES。
与输入参数集合连接
应用程序可以把少量复合参数组织为行集,再与表连接:
SELECT p.product_id, p.sku, requested.quantity
FROM products AS p
JOIN (
VALUES
(:sku_1, :quantity_1),
(:sku_2, :quantity_2),
(:sku_3, :quantity_3)
) AS requested(sku, quantity)
ON requested.sku = p.sku;
它适合一次校验少量“商品编号 + 数量”组合。所有值都必须通过驱动参数绑定,不应直接拼接用户输入。
参数很多时,SQL 文本长度、参数数量、解析时间和网络开销都会增长。大量数据更适合数据库提供的批量写入接口、数组或表值参数、临时表、文件导入等机制。
与 CTE 配合
给 VALUES 结果命名后,后续查询会更易读:
WITH status_map(status_code, status_name, sort_order) AS (
VALUES
('pending', '待支付', 1),
('paid', '已支付', 2),
('cancelled', '已取消', 3)
)
SELECT o.order_id, m.status_name
FROM orders AS o
JOIN status_map AS m ON m.status_code = o.status
ORDER BY m.sort_order, o.order_id;
CTE 列表中的三个名称依次对应每个值列表中的三个位置。它们的数量必须一致。
VALUES 与 UNION ALL
下面两种写法都能构造常量行集:
VALUES
(1, 'A'),
(2, 'B');
SELECT 1 AS id, 'A' AS name
UNION ALL
SELECT 2, 'B';
表达纯常量数据时,VALUES 更短、更清楚。需要每个分支独立读取表、使用不同过滤逻辑,或者目标数据库不支持所需的派生 VALUES 语法时,才考虑组合查询。
排序与行数限制
VALUES 构造器中行的书写顺序不应被当作结果顺序。需要稳定顺序时,使用外层 ORDER BY:
SELECT v.priority, v.label
FROM (
VALUES
(2, '普通'),
(1, '紧急'),
(3, '低')
) AS v(priority, label)
ORDER BY v.priority;
某些数据库允许直接在独立 VALUES 后添加 ORDER BY、LIMIT 或 FETCH,但外层查询形式通常更明确,也更容易跨产品使用。
空行集与单列行集
每对括号表示一行,所以单列行也需要括号:
VALUES (1), (2), (3);
普通 VALUES 语法通常不能写出“零行但带有确定列类型”的空列表。需要空行集时,可以使用一个永不成立的查询:
SELECT CAST(NULL AS INTEGER) AS id
WHERE 1 = 0;
是否允许没有 FROM 的这种写法取决于数据库;也可以从任意合适的数据源配合恒假条件构造。
性能与维护边界
少量 VALUES 通常会被当作很小的内联数据源,适合测试、映射和批量参数。但行数变大后会出现问题:
- SQL 文本和解析成本增加;
- 驱动或数据库的参数数量可能达到上限;
- 查询计划可能难以复用;
- 错误定位和维护变得困难;
- 缺少正式表的约束、索引、统计信息与权限管理。
不要仅按一个固定行数决定是否使用 VALUES。还应考虑执行频率、每行列数、数据库参数限制、网络往返和数据是否需要复用。
常见错误
每行列数不一致
所有行必须具有相同形状。为可选值使用类型合适的 NULL,不能直接省略位置。
依赖隐式类型转换
混合数字、文本、日期和全空值列时,应显式 CAST,尤其是在跨数据库运行的代码中。
把书写顺序当作结果顺序
没有 ORDER BY 就没有稳定顺序,即使数据按期望顺序写在 SQL 文本中。
在多个查询中复制业务字典
内联映射适合局部、稳定的小数据。共享业务规则应进入正式表或统一的数据模型。
拼接动态值
VALUES 不是绕过参数化的理由。每个外部输入仍应通过驱动绑定,并限制批次规模。
检查清单
- 每一行是否具有相同列数;
- 各列位置的类型是否兼容且足够明确;
- 行集和列是否设置了可读别名;
- 是否需要显式
ORDER BY; DEFAULT是否只出现在目标数据库允许的上下文;- 外部数据是否全部参数化;
- 数据规模是否已超出内联
VALUES的合理范围; - 这组数据是否应该成为有约束的正式表。
VALUES 可以看作一张写在语句中的小型临时表。只要把行形状、列类型、名称和排序规则写清楚,它就能简洁地连接常量数据与关系查询。