PostgreSQL
 sql >> Cơ Sở Dữ Liệu >  >> RDS >> PostgreSQL

PostgreSQL chuyển đổi cột thành hàng? Dịch chuyển?

Dựa trên câu trả lời của tôi trên một bảng có dạng:

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

Mỗi hàng dẫn đến một cột mới để trả về. Với kiểu trả về động như thế này, khó có thể làm cho kiểu này hoàn toàn động chỉ với một lệnh gọi đến cơ sở dữ liệu. Trình bày các giải pháp với hai bước :

  1. Tạo truy vấn
  2. Thực thi truy vấn đã tạo

Nói chung, điều này bị giới hạn bởi số cột tối đa mà một bảng có thể chứa. Vì vậy, không phải là một tùy chọn cho các bảng có hơn 1600 hàng (hoặc ít hơn). Chi tiết:

  • Số lượng cột tối đa trong một truy vấn chọn PostgreSQL là bao nhiêu

Postgres 9.3 trở lên

Giải pháp động với crosstab()

  • Hoàn toàn động, hoạt động cho bất kỳ bảng nào. Cung cấp tên bảng trong hai địa điểm:
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;

Có thể được gói thành một hàm với một tham số duy nhất ...
Tạo truy vấn có dạng:

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)

Tạo ra kết quả mong muốn:

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

Giải pháp đơn giản với 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;
  • Chậm đối với các bảng có nhiều hơn một vài cột.

Tạo truy vấn dạng:

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

Kết quả tương tự.

Postgres 9.4+

Giải pháp động với crosstab()

Sử dụng cái này nếu bạn có thể. Đánh bại phần còn lại.

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;

Hoạt động với attnum thay vì các tên cột thực tế. Đơn giản hơn và nhanh hơn. Nối kết quả với pg_attribute một lần nữa hoặc tích hợp các tên cột như trong ví dụ pg 9.3.
Tạo truy vấn có dạng:

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

Điều này sử dụng một loạt các tính năng nâng cao. Quá nhiều thứ để giải thích.

Giải pháp đơn giản với unnest()

Một unnest() bây giờ có thể lấy nhiều mảng để tách song song.

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;

Kết quả:

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 chạy trên trang 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. PostgreSQL - giá trị động dưới dạng tên bảng

  2. PostgreSQL:truyền chuỗi thành ngày DD / MM / YYYY

  3. Máy chủ Postgres không phản hồi yêu cầu nodejs

  4. Truy vấn phải để lấy số lượng kết nối hiện tại trong Cơ sở dữ liệu PostgreSQL

  5. Chuyển nhiều giá trị trong một tham số duy nhất