我在尝试UPDATE
之后尝试返回一组行。
像这样。
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函数,不能接受集合
我该怎么做呢?
即RETURNING
之后SELECT
来自UPDATE
ed数组的一组行?{/ 1>
我知道某个功能可以使用RETURNS SETOF
来实现这一点,但如果可能的话,我宁愿不这样做。
答案 0 :(得分:2)
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;
答案 1 :(得分:1)
与@klin provided一样,请使用data-modifying CTE。
但是当可以在SELECT
列表中使用set-returns函数时(由于历史原因),最好将其移至FROM
列表,如果在所有可能的。自LATERAL
的出现加入Postgres 9.3 以来几乎总是可能的。
特别是如果您需要提取多个列(来自您评论的行类型)。 <{1}}多次调用也是低效的。
unnest()
如果数组可以为空并且您想保留空/ NULL结果,请使用WITH upd AS (
UPDATE notis n
SET notis = '{}'::noti_record_type[] -- explicit cast optional
FROM (
SELECT 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
:
此外,同一LEFT JOIN LATERAL ... ON true
中的多个集合返回函数可能会出现令人惊讶的行为。避免这样做。如果你真的需要并行删除多个数组:
相关:
Postgres在将一个行类型(或复合或记录类型)从set-returns函数指定给列列表时有一个奇怪。可以预期行类型字段被视为一个列并分配给相应的列,但事实并非如此。它被自动分解(仅一个行层!)并逐个元素地分配。
所以这不能按预期工作:
SELECT
但这样做(like @klin commented):
SELECT (my_row).*
FROM upd u, unnest(u.notis) n(my_row);
或者我最终使用的更简单的版本:
SELECT (my_row).*
FROM upd u, unnest(u.notis) my_row;
还有另一种奇怪:当只为一个列的返回函数提供一个别名时,似乎用作 table 别名和为列别名。 SELECT n.*
FROM upd u, unnest(u.notis) n;
列表中的名称将返回列值。所以这些都做同样的事情:
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);
困惑?还有更多。对于复合类型(手头的情况),使用复合类型,如:
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;
那是我遇到的错误:当提供列列表时,Postgres会分解行并逐个分配提供的名称。引用SELECT n FROM unnest(ARRAY[(1, 'foo')::my_type, (2, 'bar')::my_type]) n(n);
列表中的n
不返回复合类型,而只返回(重命名)第一个元素。我粗心地期望行类型并试图用SELECT
进行分解 - 尽管如此,它只返回第一个元素。
然后再说:
(my_row).*
(请注意,第一个元素已重命名为SELECT t FROM unnest(ARRAY[(1, 'foo')::my_type, (2, 'bar')::my_type]) t(n);
!)
SQL Fiddle可以玩。
在Postgres 9.4+(不是小提琴)中,新形式的"n"
采用多个数组参数:
unnest()
但不是:
SELECT *
FROM unnest (ARRAY[(1, 'foo')::my_type, (2, 'bar')::my_type]
, ARRAY[(3, 'baz')::my_type, (4, 'bak')::my_type]) n;
我不太喜欢所有这些都是如何处理的。它经常让包括我在内的人感到困惑,而且我多年来一直在使用它。
答案 2 :(得分:0)
大概 为:
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);