[英]postgres - pivot query with array values
假设我有这张桌子:
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 |
+----+------------+----------+-----------|
事情是:我想查询内容,将“字段”中的所有行转换为列,其内容类似于:
+----+-------+----------+---------------------+
| id | title | subtitle | tags |
+----+-------+----------+---------------------+
| 1 | lorem | ipsum | [tag1,tag2,tag3] |
+----+-------+----------+---------------------|
另外,字幕和标签只是示例。 我可以根据需要设置任意多个字段,无论是否为数组。
但是我还没有找到一种将重复的“名称”值转换为数组的方法,甚至还没有将“字幕”转换为数组的方法。 如果不可能的话,“字幕”也可以变成一个数组,我可以稍后在代码上更改它,但是我至少需要以某种方式对所有内容进行分组。 有任何想法吗?
您可以使用array_agg
,例如
SELECT id_content, array_agg(value)
FROM fields
WHERE name = 'tags'
GROUP BY id_content
如果您也需要字幕,请使用自连接。 我有一个subselect来应付没有任何标签的字幕,而不会返回填充有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
如果subtitle
值是您要分隔的唯一“常量”,则可以执行以下操作:
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
);
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.