PostgreSQL
 sql >> Baza danych >  >> RDS >> PostgreSQL

PostgreSQL konwertuje kolumny na wiersze? Transponować?

Opierając swoją odpowiedź na tabeli w formularzu:

CREATE TABLE tbl (
   sl_no int
 , username text
 , designation text
 , salary int
);

Każdy wiersz powoduje wyświetlenie nowej kolumny do zwrócenia. Przy takim dynamicznym typie zwracanym, trudno jest uczynić go całkowicie dynamicznym za pomocą jednego wywołania bazy danych. Demonstrowanie rozwiązań w dwóch krokach :

  1. Generuj zapytanie
  2. Wykonaj wygenerowane zapytanie

Ogólnie jest to ograniczone przez maksymalną liczbę kolumn, które może pomieścić tabela. Więc nie jest to opcja dla tabel z więcej niż 1600 wierszami (lub mniej). Szczegóły:

  • Jaka jest maksymalna liczba kolumn w zapytaniu wybierającym PostgreSQL

Postgres 9.3 lub starszy

Dynamiczne rozwiązanie z crosstab()

  • Całkowicie dynamiczny, działa dla każdego stołu. Podaj nazwę tabeli w dwóch miejsca:
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;

Może być opakowany w funkcję z jednym parametrem...
Generuje zapytanie w postaci:

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)

Daje pożądany wynik:

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

Proste rozwiązanie z 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;
  • Wolna dla tabel z więcej niż kilkoma kolumnami.

Generuje zapytanie w postaci:

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

Ten sam wynik.

Postgres 9.4+

Dynamiczne rozwiązanie z crosstab()

Użyj tego, jeśli możesz. Pokonuje resztę.

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;

Praca z attnum zamiast rzeczywistych nazw kolumn. Prostszy i szybszy. Dołącz wynik do pg_attribute jeszcze raz lub zintegruj nazwy kolumn, jak w przykładzie na stronie 9.3.
Generuje zapytanie w postaci:

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);

Wykorzystuje całą gamę zaawansowanych funkcji. Zbyt wiele do wyjaśnienia.

Proste rozwiązanie z unnest()

Jeden unnest() może teraz rozpakowywać równolegle wiele tablic.

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;

Wynik:

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)

Skrzypce SQL działa na stronie 9.3.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Śledzenie PostgreSQL z perf

  2. użyj polecenia nazwa_bazy_danych w PostgreSQL

  3. W jaki sposób pgBouncer pomaga przyspieszyć działanie Django?

  4. Sekwencje, na które nie mają wpływu transakcje?

  5. szybki losowy wybór wiersza w Postgresie