values可直接构造无表依赖的静态数据集,如select * from (values ('user',1),('admin',2)) as t(role,level),轻量可控,跨库兼容性好,避免union all类型推导风险。

用 VALUES 构造无表依赖的静态数据集
SELECT 本身不写文件,但你可以用 VALUES 直接生成一行或多行常量数据,完全不依赖任何物理表。这是最轻量、最可控的方式,适合写测试数据、枚举值、配置快照或 SQL 脚本内联初始化。
常见错误是试图用 SELECT 'a', 123 单独执行——在多数数据库(如 PostgreSQL、SQL Server)中这会报错,因为缺少 FROM 子句;MySQL 允许,但行为不一致,不可靠。
- PostgreSQL / SQL Server / DuckDB:必须用
VALUES (…), (…)或包装成子查询:SELECT * FROM (VALUES ('user', 1), ('admin', 2)) AS t(role, level) - MySQL:支持
SELECT 'x' AS col1, 123 AS col2,但跨版本兼容性差,8.0+ 才稳定支持VALUES表值构造器 - 列名需显式指定别名(用
AS),否则导出时字段名可能为空或为默认表达式(如?'x')
导出到文件的关键不在 SELECT,而在客户端工具链
SQL 标准里没有「导出」语义,SELECT 只返回结果集。真正落地为 CSV/TSV/JSON 文件,取决于你用的客户端怎么处理这个结果流。
- psql(PostgreSQL):
\copy (VALUES ('a',1),('b',2)) TO '/tmp/data.csv' WITH (FORMAT csv, HEADER true)—— 注意是\copy,不是COPY(后者需服务端权限) - mysql 客户端:
mysql -e "SELECT * FROM (VALUES ROW('x',1), ROW('y',2)) AS t(a,b)" --batch --raw > data.tsv,再用sed或awk清理制表符前缀 - DBeaver / DataGrip:直接右键结果集 → “Export resultset”,选格式和路径,底层调的是 JDBC 的
ResultSet流式读取,跟 SQL 写法无关
避免用 UNION ALL 拼接多行静态数据
有人用 SELECT 'a' AS x, 1 AS y UNION ALL SELECT 'b', 2 模拟多行,逻辑可行但隐患明显:每条 SELECT 都触发一次隐式类型推导,容易因某一行字段类型不一致(比如第 2 行把 1 写成 '1')导致整条语句失败,且可读性和维护性远不如 VALUES。
-
VALUES是原子结构,所有行共享同一类型上下文,自动对齐 -
UNION ALL在某些引擎(如 SQLite)中无法推断列名,导出时首行可能是空标题 - 超过 5 行就该考虑用临时表或外部数据源了,硬写 SQL 不是它的设计场景
注意 NULL 和字符串转义在导出时的表现
即使数据是静态的,不同导出方式对 NULL、单引号、换行符的处理差异极大。比如 VALUES ('O''Reilly', NULL) 在 psql 的 \copy 中能正确转义,但在 mysql 的 --batch 模式下,NULL 会输出为字面量 NULL 字符串而非空字段。
- 导出前确认目标格式规范:CSV 要求
NULL映射为空字符串还是\N?字段含逗号是否加双引号? - PostgreSQL
\copy … WITH (NULL '')可控,MySQL 基本没选项,得靠后处理 - 如果下游是 Python pandas 或 Excel,优先导出为 TSV(制表符分隔),天然避开逗号和引号歧义
实际用的时候,先想清楚:你要的是“能跑通的 SQL 片段”,还是“可复用的数据交付物”。前者用 VALUES + 客户端导出命令即可;后者建议把静态数据抽离成独立 CSV 文件,SQL 里只做 COPY 或 LOAD DATA 导入——毕竟 SQL 不是数据存储格式。










