从SQL子查询中展开行

时间:2015-09-04 22:42:19

标签: sql postgresql

我有一个总是返回单个数组的SQL子查询。我希望我的外部查询过滤该数组中值的位置。我期望工作的任何东西都是为此而努力。

设置最小测试:

CREATE TABLE test (num int);
INSERT INTO test (num) values (1);
INSERT INTO test (num) values (2);

我的尝试查询,应返回两行," 1"和" 2":

SELECT * FROM test WHERE num = ANY(SELECT array_agg(num) from test);

当然...... ERROR: operator does not exist: integer = integer[]。子查询返回一行包含数组,我需要获取数组。

也尝试了这个没有成功:

SELECT * FROM test WHERE num = ANY((SELECT array_agg(num) from test)[0]);

我得到ERROR: op ANY/ALL (array) requires array on right side。这对我没有意义,但我不是专家。

我应该在这做什么?

2 个答案:

答案 0 :(得分:4)

第一个错误是由于PostgreSQL看到了

num = ANY(SELECT array_agg(num) from test)

作为

的一种形式
expression operator ANY (subquery)

而不是你想要的那个:

expression operator ANY (array expression)

因此,它会尝试将num与子查询返回的任何 ROW 进行比较(在您的示例中返回单行ARRAY[1,2]),而不是每个元素数组(因此你得到operator does not exist: integer = integer[]错误)。有关详细信息,请参阅the documentation

第二个错误只是来自[0]访问integer[]的元素的事实,因此返回integer。由于右操作数必须是一个数组(或子查询,但PostgreSQL不能在这里看到一个),PostgreSQL返回ERROR: op ANY/ALL (array) requires array on right side
(顺便说一句,数组在PostgreSQL中基于1,而不是从0开始。)

如果您确定您的函数将始终返回单个数组,那么您只需强制Postgre查看SELECT array_agg(num) from test 作为integer[]

SELECT * FROM test 
WHERE num = ANY((SELECT array_agg(num) from test)::integer[]);
┌─────┐
│ num │
├─────┤
│   1 │
│   2 │
└─────┘
(2 rows)

但请注意,如果您的函数返回多个数组,则会出现错误(因为setof integer[]不能被视为integer[]):

SELECT * FROM test
WHERE num = ANY((SELECT array_agg(num) from test GROUP BY num)::integer[]);
ERROR:  21000: more than one row returned by a subquery used as an expression
LOCATION:  ExecSetParamPlan, nodeSubplan.c:970

另一种解决方案是使用unnest函数将数组转换为一组整数,使用expression operator ANY (subquery)形式。即使你的函数返回多个数组,这也会有效,尽管它比上一个查询慢一点。

SELECT * 
FROM test 
WHERE num = ANY(
  SELECT unnest(sub.array_agg)
  FROM (
    SELECT array_agg(num) FROM test GROUP BY num -- GROUP BY num to show off the multiple array scenario
  ) AS sub
);
┌─────┐
│ num │
├─────┤
│   1 │
│   2 │
└─────┘
(2 rows)

答案 1 :(得分:1)

@Marth already explained详细信息和ANY构造的一些应用程序。密切相关:

有一个更简单的变体:在FROM子句中使用子查询作为派生表,在加入中使用子查询:

SELECT t.*
FROM   test t
JOIN  (SELECT array_agg(num) arr FROM test) a ON t.num = ANY(a.arr);

如果子选择应返回多行(每个都有一个数组),这甚至可以工作。您获得 test中与该数组的任何元素相匹配的每一行,但您只能 一次 。对于数组“{1,1,2}”,您将获得与“{1,2}”相同的两个行。

unnest() 加入到没有ANY的未加网元素。方便地使用相同的列名称并与USING子句连接以合并已连接的列。然后,您可以SELECT *获取仅test的行:

SELECT *
FROM   test t
JOIN   unnest((SELECT array_agg(num) arr FROM test)) num USING (num);

这里为数组的每个元素得到一行,所以你得到三个结果行为'{1,1,2}'。

SQL Fiddle.

(SELECT array_agg(num) arr FROM test)只是用于概念验证的虚拟子查询。但至少应该提到一次:如果可能的话,不要首先聚合,那么你不必在以后再删除。像:

SELECT *
FROM   test t
JOIN   (SELECT num FROM test) t1 USING (num);