PostgreSQL
 sql >> Teknologi Basis Data >  >> RDS >> PostgreSQL

PostgreSQL mengonversi kolom menjadi baris? Mengubah urutan?

Mendasarkan jawaban saya pada tabel berbentuk:

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

Setiap baris menghasilkan kolom baru untuk dikembalikan. Dengan tipe pengembalian dinamis seperti ini, hampir tidak mungkin untuk membuat ini sepenuhnya dinamis dengan satu panggilan ke database. Mendemonstrasikan solusi dengan dua langkah :

  1. Buat kueri
  2. Jalankan kueri yang dibuat

Umumnya, ini dibatasi oleh jumlah kolom maksimum yang dapat ditampung oleh sebuah tabel. Jadi bukan pilihan untuk tabel dengan lebih dari 1600 baris (atau kurang). Detail:

  • Berapa jumlah maksimum kolom dalam kueri pemilihan PostgreSQL

Postgres 9.3 atau lebih lama

Solusi dinamis dengan crosstab()

  • Sepenuhnya dinamis, berfungsi untuk tabel apa pun. Berikan nama tabel dalam dua tempat:
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;

Dapat dibungkus menjadi fungsi dengan satu parameter ...
Menghasilkan kueri dalam bentuk:

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)

Menghasilkan hasil yang diinginkan:

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

Solusi sederhana dengan 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;
  • Lambat untuk tabel dengan lebih dari beberapa kolom.

Menghasilkan kueri dalam bentuk:

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

Hasil yang sama.

Postgres 9.4+

Solusi dinamis dengan crosstab()

Gunakan ini jika Anda bisa. Mengalahkan sisanya.

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;

Beroperasi dengan attnum alih-alih nama kolom yang sebenarnya. Lebih sederhana dan lebih cepat. Gabungkan hasilnya ke pg_attribute sekali lagi atau integrasikan nama kolom seperti pada contoh pg 9.3.
Menghasilkan kueri dalam bentuk:

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

Ini menggunakan berbagai macam fitur canggih. Terlalu banyak untuk dijelaskan.

Solusi sederhana dengan unnest()

Satu unnest() sekarang dapat mengambil beberapa larik untuk dihapus secara paralel.

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;

Hasil:

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 berjalan pada hal 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. Bagaimana cara mengimpor modul atau menginstal ekstensi di PostgreSQL 9.1+?

  2. Pembuatan data dan kualitas perangkat keras

  3. PostgreSQL:antara dengan datetime

  4. Cara mengambil cadangan fungsi hanya di Postgres

  5. Apa cara yang tepat untuk menggunakan modul postgresql node.js?