UNNEST是PostgreSQL中将数组转为行集的核心函数,须置于FROM子句并显式别名;不支持多维数组、嵌套调用;需用LATERAL关联原表,多数组展开须同传以索引对齐;注意类型一致、空数组返回零行及性能限制。

UNNEST函数的基本用法和常见错误
UNNEST 是 PostgreSQL 中把数组转成行集的核心函数,不是语法糖,是真实执行计划里的节点。直接写 SELECT UNNEST(my_array) 会报错:「more than one row returned by a subquery used as an expression」——因为 UNNEST 返回多行,不能当标量用。
必须把它放在 FROM 子句里,当作一个“表”来 JOIN 或独立查询:
SELECT * FROM UNNEST(ARRAY['a','b','c']) AS t(val);
- 必须显式给结果列起别名(
AS t(val)),否则列名默认是unnest,容易冲突 - 传入空数组
ARRAY[]::text[]时,UNNEST返回零行,不是NULL;这点和LEFT JOIN配合时要特别注意 - 不支持直接展开多维数组,
UNNEST(ARRAY[[1,2],[3,4]])会报错「cannot unnest array of arrays」
和原表关联时怎么保持原始行不丢失
想把某列的数组展开,同时保留原表其他字段(比如用户ID + 标签数组),得用 LATERAL。没它的话,JOIN UNNEST(...) 会变成 CROSS JOIN,产生笛卡尔积。
SELECT u.id, u.name, tag FROM users u, LATERAL UNNEST(u.tags) AS tag;
-
LATERAL是关键,它让UNNEST能引用左边表的列(如u.tags) - 逗号写法是隐式
CROSS JOIN LATERAL,如果希望原表某行无数组也保留(显示 NULL),得改用LEFT JOIN LATERAL - PostgreSQL 15 支持在
LEFT JOIN LATERAL中对空数组返回 NULL 行,但需配合ON TRUE或显式条件,否则空数组仍被过滤掉
展开多个同长度数组并按索引对齐
如果手上有两个数组(比如 names 和 ages),想让 names[1] 和 ages[1] 在同一行,不能简单写两个 UNNEST —— 它们会各自独立展开,长度不一致时行为不可控。
正确做法是用 UNNEST 同时传多个数组:
SELECT name, age FROM UNNEST(ARRAY['Alice','Bob'], ARRAY[30,25]) AS t(name, age);
- 多个数组传给同一个
UNNEST,PostgreSQL 会按索引位置配对,自动截断到最短数组长度 - 若数组长度不同,长数组多余部分被丢弃,不会报错也不会补 NULL
- 不能混用标量和数组:比如
UNNEST(col1, ARRAY[...])会失败,左边也得是数组表达式
性能和类型注意事项
UNNEST 在 PostgreSQL 15 中已深度优化,但仍有几个实际影响性能的点:
- 对超大数组(比如 >10k 元素),
UNNEST会一次性加载全部元素进内存,可能触发 work_mem 溢出;建议先用array_length做长度校验或分批处理 - 数组元素类型必须严格一致,
UNNEST(ARRAY[1, 'hello'])会报错「array must have same type for all elements」 - JSONB 数组不能直接传给
UNNEST,得先用jsonb_path_query_array或jsonb_array_elements转成 SQL 数组或集合 - 如果只是想检查某个值是否在数组里,用
val = ANY(arr)比UNNEST+IN快得多,且走索引友好
最常被忽略的是:UNNEST 不支持嵌套调用,比如 UNNEST(UNNEST(nested_array)) 是非法语法——真要展平二维结构,得靠递归 CTE 或自定义函数。

















