This query generates the desired result:
SELECT id, scenario, period, p1, p2, p3, p4 -- all except aux column rn FROM crosstab( 'SELECT row_number() OVER (ORDER BY id, scenario, period)::int AS rn , id, scenario, period, period, ct FROM m ORDER BY 1' , 'VALUES (1), (2), (3), (4)' ) AS (rn int, id int, scenario int, period int, p1 int, p2 int, p3 int, p4 int);
Two special difficulties:
You do not yet have a unique column for the row name. I use row_number() to generate a surrogate key: rn . I removed it from an external SELECT according to your desired result.
The way you tried it, id taken as the name of the line, and all input lines are aggregated into one output line.
As a result, you will get additional columns ( scenario and period ), which should appear after the row_name and before the category. You must list period twice to get the original column extra - redundantly, as if it might seem.
The basis:
In connection with this particular case:
As a rule, you will have this request:
SELECT id, scenario, p1, p2, p3, p4 -- all except aux column rn FROM crosstab( 'SELECT rank() OVER (ORDER BY id, scenario)::int AS rn , id, scenario, period, ct FROM m ORDER BY 1' , 'VALUES (1), (2), (3), (4)' ) AS (rn int, id int, scenario int, p1 int, p2 int, p3 int, p4 int);
With an output similar to this:
id scenario p1 p2 p3 p4 2 1 1 1 1 1 2 2 1 1 1 1 2 3 1 1 1 1
Note the use of rank() instead of row_number() to group the same combinations (id, scenario) together.
The result makes sense if the calculations are not all 1 .