Join the Stack Overflow Community
Stack Overflow is a community of 6.6 million programmers, just like you, helping each other.
Join them; it only takes a minute:
Sign up

Suppose I have this table:

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

And this one:

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

The thing is: i want to query the content, transforming all the rows from "Fields" into columns, having something like:

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

Also, subtitle and tags are just examples. I can have as many fields as I desired, them being array or not.

But I haven't found a way to convert the repeated "name" values into an array, even more without transforming "subtitle" into array as well. If that's not possible, "subtitle" could also turn into an array and I could change it later on the code, but I needed at least to group everything somehow. Any ideas?

share|improve this question
    
subtitle is the only special "constant" that you would like to separate ? – Houari Nov 11 '14 at 12:17
    
@Houari, no, I can have as many rows in "Fields" as I want. For example: subtitle, description, image, tags (array), dimensions (array), etc.. That's why it's a little more complex than it seems – brunoramos Nov 11 '14 at 13:23

If the subtitle value is the only "constant" that you wan to separate, you can do:

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
);
share|improve this answer
    
It's not the only one. I cant have as many as I want, some being array and some not. – brunoramos Nov 11 '14 at 13:24
    
Apparently I began editing my answer and read yours afterwards. – tsnorri Nov 11 '14 at 15:55

You can use array_agg, e.g.

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

If you need the subtitle, too, use a self-join. I have a subselect to cope with subtitles that don't have any tags without returning arrays filled with NULLs, i.e. {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';

See http://www.postgresql.org/docs/9.3/static/functions-aggregate.html for details.

If you have access to the tablefunc module, another option is to use crosstab as pointed out by Houari. You can make it return arrays and non-arrays with something like this:

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

However, crosstab requires that the values always appear in the same order. For instance, if the first group (with the same id_content) doesn't have a subtitle and only has tags, the tags will be unnested and will appear in the same column with the subtitles.

See also http://www.postgresql.org/docs/9.3/static/tablefunc.html

share|improve this answer
    
The "subtitle" and "tags" are just examples. I can have 10 different fields, with 4 being array and 6 just strings, for example. That's why I was thinking about pivot. I'll try to use it with array_agg – brunoramos Nov 11 '14 at 13:22

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.