Mijn antwoord baseren op een tabel van het formulier:
CREATE TABLE tbl (
sl_no int
, username text
, designation text
, salary int
);
Elke rij resulteert in een nieuwe kolom om terug te keren. Met een dynamisch retourtype als dit is het nauwelijks mogelijk om dit volledig dynamisch te maken met een enkele aanroep van de database. Oplossingen demonstreren met twee stappen :
- Zoekopdracht genereren
- Gegenereerde zoekopdracht uitvoeren
Over het algemeen wordt dit beperkt door het maximale aantal kolommen dat een tabel kan bevatten. Dus geen optie voor tabellen met meer dan 1600 rijen (of minder). Details:
- Wat is het maximum aantal kolommen in een PostgreSQL-selectiequery
Postgres 9.3 of ouder
Dynamische oplossing met crosstab()
- Volledig dynamisch, werkt voor elke tafel. Geef de tabelnaam op in twee plaatsen:
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;
Kan worden verpakt in een functie met een enkele parameter ...
Genereert een query van de vorm:
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)
Produceert het gewenste resultaat:
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
Eenvoudige oplossing met 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;
- Traag voor tabellen met meer dan een paar kolommen.
Genereert een query van het formulier:
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
Zelfde resultaat.
Postgres 9.4+
Dynamische oplossing met crosstab()
Gebruik dit als je kunt. Verslaat de 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;
Werkend met attnum
in plaats van echte kolomnamen. Eenvoudiger en sneller. Voeg het resultaat toe aan pg_attribute
nogmaals of integreer kolomnamen zoals in het voorbeeld van pg 9.3.
Genereert een query van het formulier:
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);
Dit maakt gebruik van een hele reeks geavanceerde functies. Gewoon te veel om uit te leggen.
Eenvoudige oplossing met unnest()
Eén unnest()
kan nu meerdere arrays parallel gebruiken om te unnesten.
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;
Resultaat:
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 draait op pagina 9.3.