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 :
- Tạo truy vấn
- 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.