postgres - 使用数组值进行数据透视查询

时间:2014-11-11 11:27:37

标签: sql postgresql pivot

假设我有这张表:

Content
+----+---------+
| id |  title  |
+----+---------+
| 1  | lorem   |
+----|---------|

这一个:

Fields
+----+------------+----------+-----------+
| id | id_content |   name   |   value   |
+----+------------+----------+-----------+
| 1  |      1     | subtitle |   ipsum   |
+----+------------+----------+-----------|
| 2  |      1     |   tags   |   tag1    |
+----+------------+----------+-----------|
| 3  |      1     |   tags   |   tag2    |
+----+------------+----------+-----------|
| 4  |      1     |   tags   |   tag3    |
+----+------------+----------+-----------|

问题是:我想查询内容,将“Fields”中的所有行转换为列,具有以下内容:

+----+-------+----------+---------------------+
| id | title | subtitle |        tags         |
+----+-------+----------+---------------------+
|  1 | lorem | ipsum    | [tag1,tag2,tag3]    |
+----+-------+----------+---------------------|

此外,字幕和标签只是示例。我可以拥有任意数量的字段,无论是否为数组。

但我还没有找到一种方法将重复的“名称”值转换为数组,更不用将“副标题”转换为数组。如果这不可能,“副标题”也可以变成一个数组,我可以稍后在代码上更改它,但我至少需要以某种方式对所有内容进行分组。有什么想法吗?

2 个答案:

答案 0 :(得分:1)

您可以使用array_agg,例如

SELECT id_content, array_agg(value)
FROM fields
WHERE name = 'tags'
GROUP BY id_content

如果您还需要字幕,请使用自我加入。我有一个子选择来处理没有任何标签但不返回填充了NULL的数组的字幕,即{NULL}

SELECT f1.id_content, f1.value, f2.value
FROM fields f1
LEFT JOIN (
    SELECT id_content, array_agg(value) AS value
    FROM fields
    WHERE name = 'tags'
    GROUP BY id_content
) f2 ON (f1.id_content = f2.id_content)
WHERE f1.name = 'subtitle';

有关详细信息,请参阅http://www.postgresql.org/docs/9.3/static/functions-aggregate.html

如果您有权访问tablefunc模块,则另一个选项是使用Houari指出的crosstab。您可以使用以下内容返回数组和非数组:

SELECT id_content, unnest(subtitle), tags
FROM crosstab('
    SELECT id_content, name, array_agg(value)
    FROM fields
    GROUP BY id_content, name
    ORDER BY 1, 2
') AS ct(id_content integer, subtitle text[], tags text[]);

但是,crosstab要求值始终以相同的顺序出现。例如,如果第一个组(具有相同的id_content)没有字幕且只有标签,则标签将被取消,并且将与字幕显示在同一列中。

另见http://www.postgresql.org/docs/9.3/static/tablefunc.html

答案 1 :(得分:1)

如果subtitle值是唯一的#34;常数#34;你要分开,你可以这样做:

SELECT * FROM crosstab
(
  'SELECT content.id,name,array_to_string(array_agg(value),'','')::character varying FROM content    inner join 
    (
    select * from fields where fields.name = ''subtitle''  
        union all  
    select * from fields where fields.name <> ''subtitle'' 
    ) fields_ordered 
   on fields_ordered.id_content = content.id   group by content.id,name'
)
AS
(
       id integer,
       content_name character varying,
       tags character varying
);