Meine Antwort basiert auf einer Tabelle der Form:
CREATE TABLE tbl (
sl_no int
, username text
, designation text
, salary int
);
Jede Zeile führt zu einer neuen Spalte, die zurückgegeben werden muss. Bei einem dynamischen Rückgabetyp wie diesem ist es kaum möglich, dies mit einem einzigen Aufruf der Datenbank vollständig dynamisch zu machen. Lösungen in zwei Schritten demonstrieren :
- Abfrage generieren
- Generierte Abfrage ausführen
Im Allgemeinen ist dies durch die maximale Anzahl von Spalten begrenzt, die eine Tabelle enthalten kann. Also keine Option für Tabellen mit mehr als 1600 Zeilen (oder weniger). Einzelheiten:
- Was ist die maximale Anzahl von Spalten in einer PostgreSQL-Auswahlabfrage
Postgres 9.3 oder älter
Dynamische Lösung mit crosstab()
- Vollständig dynamisch, funktioniert für jede Tabelle. Geben Sie den Tabellennamen in zwei an Orte:
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;
Könnte in eine Funktion mit einem einzigen Parameter verpackt werden ...
Erzeugt eine Abfrage der 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)
Erzeugt das gewünschte Ergebnis:
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
Einfache Lösung mit 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;
- Langsam für Tabellen mit mehr als ein paar Spalten.
Erzeugt eine Abfrage der 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
Gleiches Ergebnis.
Postgres 9.4+
Dynamische Lösung mit crosstab()
Verwenden Sie dies, wenn Sie können. Schlagt den 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;
Betrieb mit attnum
anstelle der tatsächlichen Spaltennamen. Einfacher und schneller. Verbinden Sie das Ergebnis mit pg_attribute
noch einmal oder Spaltennamen integrieren wie im Beispiel pg 9.3.
Erzeugt eine Abfrage der 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);
Dies nutzt eine ganze Reihe von erweiterten Funktionen. Einfach zu viel zum Erklären.
Einfache Lösung mit unnest()
Ein unnest()
kann jetzt mehrere Arrays parallel zum Entschachteln verwenden.
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;
Ergebnis:
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-Geige läuft auf Seite 9.3.