Postgres - Transpose Rows to Columns

I am trying to convert rows to columns in postgres using crosstab or any other ways

Table 1:

Order_IdOrder_line_id
11001
11
12

Table 2:

Order_IdOrder_line_idTypeAmount
11001APPLE60
11001APPLE90
11APPLE0
11ORANGE32
11KIWI45
12APPLE12
12ORANGE76
12ORANGE98

Result:

Order_IdOrder_line_idAPPLE1APPLE2ORANGE1ORANGE2KIWI1KIWI2
110016090nullnullnullnull
110null32nullnull45
1212null7698nullnull

Column names are known already but the column values might be duplicate and they should be go next to each other.

i tried hard with cross tab and json (atleast tried to bring in json) couldnt progress. any help pls?

I tried to transpose rows to columns but the columns values may be duplicate. duplicate values must still be in separate column. I tried to achieve in crosstab but it didnt work

1 Answer

Your problem cannot be resolved with crosstab because the list of the columns in the result must be dynamically calculated against the rows of the Table 2 which may be updated at any time.

A solution exists to solve your problem. It consists of :

  1. creating a composite type dynamically with the list of expected column labels according to the Table 2 status within a plpgsqlprocedure
  2. calling the procedure before executing the query
  3. building the query by grouping the Table 2 rows by Order_id and Order_line_id so that to aggregate these rows into the taget row structure
  4. converting the target rows into json objects and displaying these json objects in the final status by using the json_populate_record function and the composite type

Step 1 :

CREATE OR REPLACE PROCEDURE composite_type() LANGUAGE plpgsql AS $$
DECLARE column_list text ;
BEGIN
SELECT string_agg(m.name || ' text', ', ') INTO column_list FROM ( SELECT Type || generate_series(1, max(count)) AS name FROM ( SELECT lower(Type) AS type, count(*) FROM table_2 GROUP BY Order_Id, Order_line_id, Type ) AS s GROUP BY Type ) AS m ;
DROP type IF EXISTS composite_type ;
EXECUTE 'CREATE type composite_type AS (' || column_list || ')';
END ; $$

Step 2 :

CALL composite_type() ;

Step 3,4 :

SELECT t.Order_Id, t.Order_line_id , (json_populate_record(null :: composite_type, json_object_agg(t.label, t.Amount))).* FROM ( SELECT Order_Id, Order_line_id , lower(Type) || (row_number() OVER (PARTITION BY Order_Id, Order_line_id, Type)) :: text AS label , Amount FROM table_2 ) AS t GROUP BY t.Order_Id, t.Order_line_id ;

The final result is :

order_idorder_line_idkiwi1orange1orange2apple1apple2
114532null0null
12null987612null
11001nullnullnull6090

see the full test result in dbfiddle

4

Your Answer

Sign up or log in

Sign up using Google Sign up using Facebook Sign up using Email and Password

Post as a guest

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

You Might Also Like