【问题标题】:Return multiple values and populate column in array like manner返回多个值并以类似数组的方式填充列
【发布时间】:2013-11-20 12:30:58
【问题描述】:

我在 MacOSX 上使用 Postgres 9.3。

我想知道如何返回多个值(取决于某些标准)并使用它们以类似列表/数组的方式填充列?

--DUMMY DATA

CREATE TABLE tbl (
   id VARCHAR(2) PRIMARY KEY
  ,name TEXT
  ,year_born NUMERIC
  ,nationality TEXT
);
INSERT INTO tbl(id, name, year_born, nationality)
VALUES ('A1','Bill',2001,'American')
      ,('B1','Anna',1997,'Swedish')
      ,('A2','Bill',1991,'American')
      ,('B2','Anna',2004,'Swedish')
      ,('B3','Anna',1989,'Swedish')
      ,('A3','Bill',1995,'American');
SELECT * FROM tbl;

id | name | year_born | nationality
---+------+-----------+------------
A1 | Bill |   2001    |  American
B1 | Anna |   1997    |  Swedish
A2 | Bill |   1991    |  American
B2 | Anna |   2004    |  Swedish
B3 | Anna |   1989    |  Swedish
A3 | Bill |   1995    |  American

我使用SELECT DISTINCT ON 子句在name, nationality 列上汇集,如下面的代码所示

CREATE TABLE another_tbl ( name TEXT, nationality TEXT, ids VARCHAR ); 

CREATE FUNCTION f1() RETURNS SETOF another_tbl AS
$$ SELECT DISTINCT ON (name, nationality) name, nationality, id
   FROM tbl
   GROUP BY name, nationality, ID;
$$ LANGUAGE sql

SELECT * FROM f1();

 name | nationality | ids 
------+-------------+-----
 Anna |  Swedish    | B1
 Bill |  American   | A1

所以,这是我不知道如何实现的事情,但我认为这很容易。我希望 ids 列由与 name 列中的名称对应的所有 id 填充,如下所示。

期望的输出:

 SELECT * FROM f1();

 name | nationality | ids 
------+-------------+-----
 Anna |  Swedish    | B1, B2, B3
 Bill |  American   | A1, A2, A3

更新

发现了ARRAY,我将它与VARCHAR 类一起用于another_tbl 中的ids 列。但是,我接到一个不匹配的电话,说Final statement returns character varying instead ofcharacter varying[]at column 3

【问题讨论】:

    标签: sql list postgresql distinct aggregate-functions


    【解决方案1】:

    如果您想要一个文本列作为结果,请使用 GROUP BY 和聚合函数 string_agg()
    array_agg() 构造一个数组。
    但是放弃现在多余的DISTINCT ON

    SELECT name, nationality, string_agg(id, ',') AS ids
    FROM   tbl
    GROUP  BY 1, 2
    ORDER  BY 1, 2;
    

    函数定义的RETURNS 子句必须匹配,就像@ozczecho 建议的那样:

    CREATE FUNCTION f1()
      RETURNS TABLE(name text, nationality text, ids text) AS
                                                  -- varchar[] for array_agg()
    $func$
    SELECT t.name, t.nationality, string_agg(t.id, ',') AS ids
    FROM   tbl t
    GROUP  BY 1, 2
    ORDER  BY 1, 2;
    $func$ LANGUAGE sql;
    

    【讨论】:

    • 酷,胜过我的回答..感谢您的澄清!
    • 我正在尝试做同样的事情,但使用的是 MS SQL。 MS SQL 中的 string_agg() 是否有任何等价物?还是另一种 MS SQL 方法可以提供与此问题所要求的结果相似的结果?
    • @HammanSamuel:很久以前我问过这个similar question,得到了很好的答案。
    【解决方案2】:

    我认为你应该改变:

    RETURNS SETOF another_tbl
    

    到:

    RETURNS TABLE(name TEXT, nationality TEXT, ids VARCHAR[])
    

    【讨论】:

    • 感谢您的评论。但是,您的建议给出了相同的不匹配错误消息,因为在这种情况下 RETURNS TABLE(name...) 应该等同于 another_tbl。默认情况下,VARCHAR[] 会将数组设置为一维。就我而言,我事先不知道数组需要多少维。我认为您可以为此使用ARRAY
    • @jO.:目前,Postgres 中任何给定的基本类型都只有 一个 数组类型,涵盖所有维度。 Details in this related answer.
    【解决方案3】:

    好的,它使用array_agg 函数工作。

    CREATE FUNCTION f1() RETURNS SETOF another_tbl AS
    $$ SELECT DISTINCT ON (name, nationality) name, nationality, array_agg(id)
       FROM tbl
       GROUP BY name, nationality
    $$ LANGUAGE sql
    
    SELECT * FROM f();
    
    name | nationality |    ids     
    -----+-------------+-----------
    Anna | Swedish     | {B1,B2,B3}
    Bill | American    | {A1,A2,A3}
    

    在将函数 array_to_string 添加到 array_agg(id) 后,我们得到了所需的输出

    name | nationality |   ids    
    -----+-------------+---------
    Anna | Swedish     | B1,B2,B3
    Bill | American    | A1,A2,A3
    

    【讨论】:

      猜你喜欢
      • 2023-03-29
      • 1970-01-01
      • 2020-10-22
      • 1970-01-01
      • 2014-08-12
      • 1970-01-01
      • 2020-03-19
      • 1970-01-01
      相关资源
      最近更新 更多