Postgres - Transpose Rows to Columns

不羁的心 提交于 2019-11-26 22:03:28
Erwin Brandstetter

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 with row_number() in a subquery.

  • The varying number of emails.
    -> We limit to a max. of three in the outer SELECT
    and use crosstab() 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.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!