使用 unnest() 返回行?
RETURNING rows using unnest()?
在完成 UPDATE
后,我正在尝试 return 一组行。
像这样。
UPDATE Notis new_noti SET notis = '{}'::noti_record_type[]
FROM (SELECT * FROM Notis WHERE user_id = 2 FOR UPDATE) old_noti
WHERE old_noti.user_id = new_noti.user_id RETURNING unnest(old_noti.notis);
但是 postgres 抱怨,这是正确的:
set-valued function called in context that cannot accept a set
我应该如何着手实施它?
也就是说,RETURNING
来自 SELECT
ed 数组的一组行在 UPDATE
?
之后
我知道函数可以使用 RETURNS SETOF
实现此目的,但如果可能的话我宁愿不要这样做。
WITH upd AS (
UPDATE Notis new_noti SET notis = '{}'::noti_record_type[]
FROM (SELECT * FROM Notis WHERE user_id = 2 FOR UPDATE) old_noti
WHERE old_noti.user_id = new_noti.user_id RETURNING old_noti.notis
)
SELECT unnest(notis) FROM upd;
使用 data-modifying CTE.
您 可以 在 SELECT
列表中使用 set-returning 函数,但是将它移动到 FROM
列表中使用 LATERAL
自 Postgres 9.3 以来的子查询。特别是如果您需要提取 多个 列(从 行类型 中提取,就像您评论的那样)。多次调用 unnest()
也是低效的。
WITH upd AS (
UPDATE notis n
SET notis = '{}'::noti_record_type[] -- explicit cast optional
FROM (
SELECT user_id, notis
FROM notis
WHERE user_id = 2
FOR UPDATE
) old_n
WHERE old_n.user_id = n.user_id
RETURNING old_n.notis
)
SELECT n.*
FROM upd u, unnest(u.notis) n; -- implicit CROSS JOIN LATERAL
如果数组可以为空并且您想保留空/NULL 结果,请使用 LEFT JOIN LATERAL ... ON true
。参见:
- Call a set-returning function with an array argument multiple times
此外,同一个 SELECT
中的多个 set-returning 函数会表现出令人惊讶的行为。避免这种情况。
这已使用 Postgres 10 进行了清理。请参阅:
在 Postgres 10 之前和之后并行取消嵌套 多个 数组的替代方法:
相关:
- Return pre-UPDATE column values using SQL only
复合/行值的行为
Postgres 在从 set-returning 函数向列列表分配行类型(或复合或记录类型)时有一个 oddity。人们可能希望行类型字段被视为 one 列并分配给相应的列,但事实并非如此。它会自动分解(仅一行层!)并逐个元素分配。
所以这没有按预期工作:
<strike>SELECT (my_row).*
FROM upd u, unnest(u.notis) n(my_row);</strike>
但这确实 (:
SELECT (my_row).*
FROM upd u, unnest(u.notis) my_row;
或者我最终使用的更简单的版本:
SELECT n.*
FROM upd u, unnest(u.notis) n;
另一个奇怪之处:具有单个字段 的复合(或行)类型会自动分解。因此,table 别名和列别名最终在外部 SELECT
列表中执行相同的操作:
SELECT n FROM unnest(ARRAY[1,2,3]) n;
SELECT n FROM unnest(ARRAY[1,2,3]) n(n);
SELECT n FROM unnest(ARRAY[1,2,3]) t(n);
SELECT t FROM unnest(ARRAY[1,2,3]) t(n); -- except output column name is "t"
对于多个字段,保留行包装器:
SELECT t FROM unnest(ARRAY[1,2,3]) WITH ORDINALITY t(n); -- requires 9.4+
迷茫?还有更多。对于复合类型(手头的情况),如:
CREATE TYPE my_type AS (id int, txt text);
虽然这按预期工作:
SELECT n FROM unnest(ARRAY[(1, 'foo')::my_type, (2, 'bar')::my_type]) n;
您在这里有 惊喜:
SELECT n FROM unnest(ARRAY[(1, 'foo')::my_type, (2, 'bar')::my_type]) n(n);
这就是我遇到的错误:提供列列表时,Postgres 分解行并逐一分配提供的名称。在 SELECT
列表中引用 n
并不是 return 复合类型,而只是(重命名的)第一个元素。我错误地预期了行类型并尝试使用 (my_row).*
进行分解 - 尽管如此,它只有 return 是第一个元素。
再说一遍:
SELECT t FROM unnest(ARRAY[(1, 'foo')::my_type, (2, 'bar')::my_type]) t(n);
(请注意,第一个元素已重命名为 "n"
!)
使用新形式的 unnest()
接受多个数组参数(Postgres 9.4+):
SELECT *
FROM unnest(ARRAY[(1, 'foo')::my_type, (2, 'bar')::my_type]
, ARRAY[(3, 'baz')::my_type, (4, 'bak')::my_type]) n;
仅前两个输出列的列别名:
SELECT *
FROM unnest(ARRAY[(1, 'foo')::my_type, (2, 'bar')::my_type]
, ARRAY[(3, 'baz')::my_type, (4, 'bak')::my_type]) n(a, b);
所有输出列的列别名:
SELECT *
FROM unnest(ARRAY[(1,'foo')::my_type, (2,'bar')::my_type]
, ARRAY[(3,'baz')::my_type, (4,'bak')::my_type]) n(a,b,c,d);
大概
对于:
SELECT *
FROM unnest (ARRAY[(1, 'foo')::my_type, (2, 'bar')::my_type]
, ARRAY[(3, 'baz')::my_type, (4, 'bak')::my_type]) n(a, b);
使用:
SELECT *
FROM unnest (ARRAY[(1, 'foo')::text, (2, 'bar')::text]
, ARRAY[(3, 'baz')::text, (4, 'bak')::text]) WITH ORDINALITY AS t(first_col, second_col);
在完成 UPDATE
后,我正在尝试 return 一组行。
像这样。
UPDATE Notis new_noti SET notis = '{}'::noti_record_type[]
FROM (SELECT * FROM Notis WHERE user_id = 2 FOR UPDATE) old_noti
WHERE old_noti.user_id = new_noti.user_id RETURNING unnest(old_noti.notis);
但是 postgres 抱怨,这是正确的:
set-valued function called in context that cannot accept a set
我应该如何着手实施它?
也就是说,RETURNING
来自 SELECT
ed 数组的一组行在 UPDATE
?
我知道函数可以使用 RETURNS SETOF
实现此目的,但如果可能的话我宁愿不要这样做。
WITH upd AS (
UPDATE Notis new_noti SET notis = '{}'::noti_record_type[]
FROM (SELECT * FROM Notis WHERE user_id = 2 FOR UPDATE) old_noti
WHERE old_noti.user_id = new_noti.user_id RETURNING old_noti.notis
)
SELECT unnest(notis) FROM upd;
使用 data-modifying CTE.
您 可以 在 SELECT
列表中使用 set-returning 函数,但是将它移动到 FROM
列表中使用 LATERAL
自 Postgres 9.3 以来的子查询。特别是如果您需要提取 多个 列(从 行类型 中提取,就像您评论的那样)。多次调用 unnest()
也是低效的。
WITH upd AS (
UPDATE notis n
SET notis = '{}'::noti_record_type[] -- explicit cast optional
FROM (
SELECT user_id, notis
FROM notis
WHERE user_id = 2
FOR UPDATE
) old_n
WHERE old_n.user_id = n.user_id
RETURNING old_n.notis
)
SELECT n.*
FROM upd u, unnest(u.notis) n; -- implicit CROSS JOIN LATERAL
如果数组可以为空并且您想保留空/NULL 结果,请使用 LEFT JOIN LATERAL ... ON true
。参见:
- Call a set-returning function with an array argument multiple times
此外,同一个
这已使用 Postgres 10 进行了清理。请参阅:SELECT
中的多个 set-returning 函数会表现出令人惊讶的行为。避免这种情况。
在 Postgres 10 之前和之后并行取消嵌套 多个 数组的替代方法:
相关:
- Return pre-UPDATE column values using SQL only
复合/行值的行为
Postgres 在从 set-returning 函数向列列表分配行类型(或复合或记录类型)时有一个 oddity。人们可能希望行类型字段被视为 one 列并分配给相应的列,但事实并非如此。它会自动分解(仅一行层!)并逐个元素分配。
所以这没有按预期工作:
<strike>SELECT (my_row).*
FROM upd u, unnest(u.notis) n(my_row);</strike>
但这确实 (
SELECT (my_row).*
FROM upd u, unnest(u.notis) my_row;
或者我最终使用的更简单的版本:
SELECT n.*
FROM upd u, unnest(u.notis) n;
另一个奇怪之处:具有单个字段 的复合(或行)类型会自动分解。因此,table 别名和列别名最终在外部 SELECT
列表中执行相同的操作:
SELECT n FROM unnest(ARRAY[1,2,3]) n;
SELECT n FROM unnest(ARRAY[1,2,3]) n(n);
SELECT n FROM unnest(ARRAY[1,2,3]) t(n);
SELECT t FROM unnest(ARRAY[1,2,3]) t(n); -- except output column name is "t"
对于多个字段,保留行包装器:
SELECT t FROM unnest(ARRAY[1,2,3]) WITH ORDINALITY t(n); -- requires 9.4+
迷茫?还有更多。对于复合类型(手头的情况),如:
CREATE TYPE my_type AS (id int, txt text);
虽然这按预期工作:
SELECT n FROM unnest(ARRAY[(1, 'foo')::my_type, (2, 'bar')::my_type]) n;
您在这里有 惊喜:
SELECT n FROM unnest(ARRAY[(1, 'foo')::my_type, (2, 'bar')::my_type]) n(n);
这就是我遇到的错误:提供列列表时,Postgres 分解行并逐一分配提供的名称。在 SELECT
列表中引用 n
并不是 return 复合类型,而只是(重命名的)第一个元素。我错误地预期了行类型并尝试使用 (my_row).*
进行分解 - 尽管如此,它只有 return 是第一个元素。
再说一遍:
SELECT t FROM unnest(ARRAY[(1, 'foo')::my_type, (2, 'bar')::my_type]) t(n);
(请注意,第一个元素已重命名为 "n"
!)
使用新形式的 unnest()
接受多个数组参数(Postgres 9.4+):
SELECT *
FROM unnest(ARRAY[(1, 'foo')::my_type, (2, 'bar')::my_type]
, ARRAY[(3, 'baz')::my_type, (4, 'bak')::my_type]) n;
仅前两个输出列的列别名:
SELECT *
FROM unnest(ARRAY[(1, 'foo')::my_type, (2, 'bar')::my_type]
, ARRAY[(3, 'baz')::my_type, (4, 'bak')::my_type]) n(a, b);
所有输出列的列别名:
SELECT *
FROM unnest(ARRAY[(1,'foo')::my_type, (2,'bar')::my_type]
, ARRAY[(3,'baz')::my_type, (4,'bak')::my_type]) n(a,b,c,d);
大概 对于:
SELECT *
FROM unnest (ARRAY[(1, 'foo')::my_type, (2, 'bar')::my_type]
, ARRAY[(3, 'baz')::my_type, (4, 'bak')::my_type]) n(a, b);
使用:
SELECT *
FROM unnest (ARRAY[(1, 'foo')::text, (2, 'bar')::text]
, ARRAY[(3, 'baz')::text, (4, 'bak')::text]) WITH ORDINALITY AS t(first_col, second_col);