PostgreSQL中UNNEST不能直接展开多列,必须先用ARRAY[...]将各列打包为单个数组,且元素类型需兼容;配合WITH ORDINALITY可实现带序号和列名的精准展开。

PostgreSQL 中用 UNNEST 把多列转成行,不是直接“一函数搞定”,得配合 ROW 或数组构造,否则会出错或结果错位。
UNNEST 不能直接展开多列,必须先打包成数组
UNNEST(col1, col2, col3) 这种写法在 PostgreSQL 中是非法的 —— UNNEST 只接受单个数组(或多个同长度数组),不支持裸列。常见错误是误以为它像 SQL Server 的 UNPIVOT 那样能直接受多列参数。
- 正确做法:把各列用
ARRAY[col1, col2, col3]包成一个一维数组,再传给UNNEST - 如果还要保留原列名或类型信息,得额外用
WITH ORDINALITY或VALUES构造带标签的行 - 注意:所有被放进数组的列必须兼容同一数据类型(例如都转成
TEXT),否则报cannot cast type integer to text类错误
用 UNNEST + WITH ORDINALITY 实现带序号和原始列名的展开
想让结果里知道某行值来自哪一列,又不想手写一堆 UNION ALL,可以用 WITH ORDINALITY 给展开后的元素编号,再关联列名映射表。
SELECT id, cols.col_name, cols.value FROM t, LATERAL UNNEST( ARRAY['col_a', 'col_b', 'col_c'], ARRAY[col_a::TEXT, col_b::TEXT, col_c::TEXT] ) WITH ORDINALITY AS cols(col_name, value, ord);
- 第一个
ARRAY是列名列表(字符串),第二个是对应值列表(需统一类型) -
WITH ORDINALITY让UNNEST返回第 3 列ord,确保名称和值严格对齐 - 必须用
LATERAL,否则无法引用左侧表t的字段
当列数动态或很多时,UNNEST 不是最佳选择
如果要处理几十列,或者列名来自元数据(比如 information_schema.columns),硬写 ARRAY[...] 易错且不可维护。这时候应考虑:
- 用
jsonb_each先转成 JSON:SELECT * FROM jsonb_each(to_jsonb(t) - 'id'),自动忽略指定键,返回key/value对 - 但注意:
jsonb_each会强制转换所有值为 JSON 类型,数值、布尔、null 都 OK,但自定义类型(如hstore、几何类型)可能失败 -
UNNEST本身不支持跨行聚合后展开 —— 如果你先STRING_AGG再UNNEST,得到的是字符串切分结果,不是原始列语义
真正难的不是语法,而是明确你要的是“结构化列转行”还是“纯文本扁平化”。前者要保类型和来源,后者用 jsonb_each 更稳;而手写 UNNEST(ARRAY[...]) 看似简单,一旦列类型不一致或顺序错一位,结果就 silently 错了。










