I have a PostgreSQL function (or table) which gives me the following output:
Sl.no username Designation salary etc.. 1 A XYZ 10000 ... 2 B RTS 50000 ... 3 C QWE 20000 ... 4 D HGD 34343 ...
Now I want the Output as below:
Sl.no 1 2 3 4 ... Username A B C D ... Designation XYZ RTS QWE HGD ... Salary 10000 50000 20000 34343 ...
How to do this?
Unnest function generates a table structure of an array in PostgreSQL. Unnest array function is beneficial in PostgreSQL for expanding the array into the set of values or converting the array into the structure of the rows. PostgreSQL offers unnest() function.
The crosstab function produces one output row for each consecutive group of input rows with the same row_name value. The output row_name column, plus any “extra” columns, are copied from the first row of the group. The output value columns are filled with the value fields from rows having matching category values.
In PostgreSQL, the ROW_NUMBER() function is used to assign a unique integer to every row that is returned by a query. Syntax: ROW_NUMBER() OVER( [PARTITION BY column_1, column_2, …] [ORDER BY column_3, column_4, …] )
Basing my answer on a table of the form:
CREATE TABLE tbl ( sl_no int , username text , designation text , salary int );
Each row results in a new column to return. With a dynamic return type like this, it's hardly possible to make this completely dynamic with a single call to the database. Demonstrating solutions with two steps:
Generally, this is limited by the maximum number of columns a table can hold. So not an option for tables with more than 1600 rows (or fewer). Details:
crosstab()
Use the first one you can. Beats the rest.
SELECT 'SELECT * FROM crosstab( $ct$SELECT u.attnum, t.rn, u.val FROM (SELECT row_number() OVER () AS rn, * FROM ' || attrelid::regclass || ') t , unnest(ARRAY[' || string_agg(quote_ident(attname) || '::text', ',') || ']) WITH ORDINALITY u(val, attnum) ORDER BY 1, 2$ct$ ) t (attnum bigint, ' || (SELECT string_agg('r'|| rn ||' text', ', ') FROM (SELECT row_number() OVER () AS rn FROM tbl) t) || ')' AS sql FROM pg_attribute WHERE attrelid = 'tbl'::regclass AND attnum > 0 AND NOT attisdropped GROUP BY attrelid;
Operating with attnum
instead of actual column names. Simpler and faster. Join the result to pg_attribute
once more or integrate column names like in the pg 9.3 example.
Generates a query of the form:
SELECT * FROM crosstab( $ct$ SELECT u.attnum, t.rn, u.val FROM (SELECT row_number() OVER () AS rn, * FROM tbl) t , unnest(ARRAY[sl_no::text,username::text,designation::text,salary::text]) WITH ORDINALITY u(val, attnum) ORDER BY 1, 2$ct$ ) t (attnum bigint, r1 text, r2 text, r3 text, r4 text);
This uses a whole range of advanced features. Just too much to explain.
unnest()
One unnest()
can now take multiple arrays to unnest in parallel.
SELECT 'SELECT * FROM unnest( ''{sl_no, username, designation, salary}''::text[] , ' || string_agg(quote_literal(ARRAY[sl_no::text, username::text, designation::text, salary::text]) || '::text[]', E'\n, ') || E') \n AS t(col,' || string_agg('row' || sl_no, ',') || ')' AS sql FROM tbl;
Result:
SELECT * FROM unnest( '{sl_no, username, designation, salary}'::text[] ,'{10,Joe,Music,1234}'::text[] ,'{11,Bob,Movie,2345}'::text[] ,'{12,Dave,Theatre,2356}'::text[]) AS t(col,row1,row2,row3,row4);
SQL Fiddle running on pg 9.6
crosstab()
SELECT 'SELECT * FROM crosstab( ''SELECT unnest(''' || quote_literal(array_agg(attname)) || '''::text[]) AS col , row_number() OVER () , unnest(ARRAY[' || string_agg(quote_ident(attname) || '::text', ',') || ']) AS val FROM ' || attrelid::regclass || ' ORDER BY generate_series(1,' || count(*) || '), 2'' ) t (col text, ' || (SELECT string_agg('r'|| rn ||' text', ',') FROM (SELECT row_number() OVER () AS rn FROM tbl) t) || ')' AS sql FROM pg_attribute WHERE attrelid = 'tbl'::regclass AND attnum > 0 AND NOT attisdropped GROUP BY attrelid;
Could be wrapped into a function with a single parameter ...
Generates a query of the form:
SELECT * FROM crosstab( 'SELECT unnest(''{sl_no,username,designation,salary}''::text[]) AS col , row_number() OVER () , unnest(ARRAY[sl_no::text,username::text,designation::text,salary::text]) AS val FROM tbl ORDER BY generate_series(1,4), 2' ) t (col text, r1 text,r2 text,r3 text,r4 text);
Produces the desired result:
col r1 r2 r3 r4 ----------------------------------- sl_no 1 2 3 4 username A B C D designation XYZ RTS QWE HGD salary 10000 50000 20000 34343
unnest()
SELECT 'SELECT unnest(''{sl_no, username, designation, salary}''::text[] AS col) , ' || string_agg('unnest(' || quote_literal(ARRAY[sl_no::text, username::text, designation::text, salary::text]) || '::text[]) AS row' || sl_no, E'\n , ') AS sql FROM tbl;
Generates a query of the form:
SELECT unnest('{sl_no, username, designation, salary}'::text[]) AS col , unnest('{10,Joe,Music,1234}'::text[]) AS row1 , unnest('{11,Bob,Movie,2345}'::text[]) AS row2 , unnest('{12,Dave,Theatre,2356}'::text[]) AS row3 , unnest('{4,D,HGD,34343}'::text[]) AS row4
Same result.
SELECT unnest(array['Sl.no', 'username', 'Designation','salary']) AS "Columns", unnest(array[Sl.no, username, value3Count,salary]) AS "Values" FROM view_name ORDER BY "Columns"
Reference : convertingColumnsToRows
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With