I have the following table, which gives multiple email addresses for each user.

I need to flatten this out to columns on a user query. To give me the "newest" 3 email addresses based on the creation date.
user.name | user.id | email1 | email2 | email3**
Mary | 123 | mary@gmail.com | mary@yahoo.co.uk | mary@test.com
Joe | 345 | joe@gmail.com | [NULL] | [NULL]
Use crosstab()
from the tablefunc module.
SELECT * FROM crosstab(
$$SELECT user_id, user_name, rn, email_address
FROM (
SELECT u.user_id, u.user_name, e.email_address
, row_number() OVER (PARTITION BY u.user_id
ORDER BY e.creation_date DESC NULLS LAST) AS rn
FROM usr u
LEFT JOIN email_tbl e USING (user_id)
) sub
WHERE rn < 4
ORDER BY user_id
$$
, 'VALUES (1),(2),(3)'
) AS t (user_id int, user_name text, email1 text, email2 text, email3 text);
I used dollar-quoting for the first parameter, which has no special meaning. It's just convenient if you have to escape single quotes in the query string which is a common case:
Detailed explanation and instructions here:
And in particular, for "extra columns":
The special difficulties here are:
The lack of key names.
-> We substitute withrow_number()
in a subquery.The varying number of emails.
-> We limit to a max. of three in the outerSELECT
and usecrosstab()
with two parameters, providing a list of possible keys.
Pay attention to NULLS LAST
in the ORDER BY
.
If anyone else that finds this question and needs a dynamic solution for this where you have an undefined number of columns to transpose to and not exactly 3, you can find a nice solution here: https://github.com/jumpstarter-io/colpivot
the above code has really helped me. Now, I am trying to add more fields to the pivot table.
SELECT * INTO temp_pivot_table FROM crosstab(
$$SELECT lead_id, magazine_id, days_between_mailers, rn
FROM (
SELECT u.lead_id, u.magazine_id, u.days_between_mailers
, row_number() OVER (PARTITION BY u.lead_id
ORDER BY u.id ASC NULLS LAST) AS rn
FROM temp_mailer_stats u
) sub
WHERE rn <= 3
ORDER BY lead_id
$$
, 'VALUES (1),(2),(3),(4),(5),(6)'
) AS t (lead_id int, magazine1 text, magazine2 text, magazine3 text,
days_between1 integer, days_between2 integer, days_between3 integer);
Of course I can make separate tables and then join them together, but if I can save a step that would be ideal.
Thanks in advance for the help.
来源:https://stackoverflow.com/questions/23060256/postgres-transpose-rows-to-columns