跳到主要内容

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 不是绕过参数化的理由。每个外部输入仍应通过驱动绑定,并限制批次规模。

检查清单​

  1. 每一行是否具有相同列数;
  2. 各列位置的类型是否兼容且足够明确;
  3. 行集和列是否设置了可读别名;
  4. 是否需要显式 ORDER BY;
  5. DEFAULT 是否只出现在目标数据库允许的上下文;
  6. 外部数据是否全部参数化;
  7. 数据规模是否已超出内联 VALUES 的合理范围;
  8. 这组数据是否应该成为有约束的正式表。

VALUES 可以看作一张写在语句中的小型临时表。只要把行形状、列类型、名称和排序规则写清楚,它就能简洁地连接常量数据与关系查询。