使用unnest()返回行?

时间:2015-08-28 14:40:55

标签: sql database postgresql set-returning-functions sql-returning

我在尝试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来实现这一点,但如果可能的话,我宁愿不这样做。

3 个答案:

答案 0 :(得分:2)

使用WITH statement

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);