Postgres 9.4 trở lên
Sử dụng WITH ORDINALITY
cho các hàm trả về thiết lập:
Khi một hàm trong
FROM
mệnh đề có hậu tố làWITH ORDINALITY
, mộtbigint
cột được nối vào đầu ra bắt đầu từ 1 và tăng thêm 1 cho mỗi hàng của đầu ra của hàm. Điều này hữu ích nhất trong trường hợp đặt các hàm trả về chẳng hạn nhưunnest()
.
Kết hợp với LATERAL
tính năng trong pg 9.3+ và theo chủ đề này trên pgsql-hacker, truy vấn trên hiện có thể được viết là:
SELECT t.id, a.elem, a.nr
FROM tbl AS t
LEFT JOIN LATERAL unnest(string_to_array(t.elements, ','))
WITH ORDINALITY AS a(elem, nr) ON TRUE;
LEFT JOIN ... ON TRUE
giữ nguyên tất cả các hàng trong bảng bên trái, ngay cả khi biểu thức bảng bên phải trả về không có hàng nào. Nếu điều đó không có gì đáng lo ngại, bạn có thể sử dụng cách khác tương đương, bớt chi tiết hơn biểu mẫu có CROSS JOIN LATERAL
ngầm định :
SELECT t.id, a.elem, a.nr
FROM tbl t, unnest(string_to_array(t.elements, ',')) WITH ORDINALITY a(elem, nr);
Hoặc đơn giản hơn nếu dựa trên mảng thực tế (arr
là một cột mảng):
SELECT t.id, a.elem, a.nr
FROM tbl t, unnest(t.arr) WITH ORDINALITY a(elem, nr);
Hoặc thậm chí, với cú pháp tối thiểu:
SELECT id, a, ordinality
FROM tbl, unnest(arr) WITH ORDINALITY a;
a
tự động là bảng và bí danh cột. Tên mặc định của cột thứ tự đã thêm là ordinality
. Nhưng tốt hơn (an toàn hơn, rõ ràng hơn) nếu thêm bí danh cột rõ ràng và cột đủ điều kiện trong bảng.
Postgres 8,4 - 9,3
Với row_number() OVER (PARTITION BY id ORDER BY elem)
bạn nhận được các số theo thứ tự sắp xếp, không phải số thứ tự của vị trí thứ tự ban đầu trong chuỗi.
Bạn chỉ cần bỏ qua ORDER BY
:
SELECT *, row_number() OVER (PARTITION by id) AS nr
FROM (SELECT id, regexp_split_to_table(elements, ',') AS elem FROM tbl) t;
Trong khi điều này bình thường hoạt động và tôi chưa bao giờ thấy nó không thành công trong các truy vấn đơn giản, PostgreSQL khẳng định không có gì liên quan đến thứ tự của các hàng mà không có ORDER BY
. Nó hoạt động do một chi tiết triển khai.
Để đảm bảo số thứ tự trong số các phần tử trong chuỗi được phân tách bằng khoảng trống :
SELECT id, arr[nr] AS elem, nr
FROM (
SELECT *, generate_subscripts(arr, 1) AS nr
FROM (SELECT id, string_to_array(elements, ' ') AS arr FROM tbl) t
) sub;
Hoặc đơn giản hơn nếu dựa trên mảng thực tế :
SELECT id, arr[nr] AS elem, nr
FROM (SELECT *, generate_subscripts(arr, 1) AS nr FROM tbl) t;
Câu trả lời liên quan trên dba.SE:
- Làm cách nào để bảo toàn thứ tự ban đầu của các phần tử trong một mảng chưa được sắp xếp?
Postgres 8.1 - 8.4
Chưa có tính năng nào trong số những tính năng này:RETURNS TABLE
, generate_subscripts()
, unnest()
, array_length()
. Nhưng điều này hoạt động:
CREATE FUNCTION f_unnest_ord(anyarray, OUT val anyelement, OUT ordinality integer)
RETURNS SETOF record
LANGUAGE sql IMMUTABLE AS
'SELECT $1[i], i - array_lower($1,1) + 1
FROM generate_series(array_lower($1,1), array_upper($1,1)) i';
Đặc biệt lưu ý rằng chỉ số mảng có thể khác với vị trí thứ tự của các phần tử. Hãy xem xét bản trình diễn này với một chức năng mở rộng :
CREATE FUNCTION f_unnest_ord_idx(anyarray, OUT val anyelement, OUT ordinality int, OUT idx int)
RETURNS SETOF record
LANGUAGE sql IMMUTABLE AS
'SELECT $1[i], i - array_lower($1,1) + 1, i
FROM generate_series(array_lower($1,1), array_upper($1,1)) i';
SELECT id, arr, (rec).*
FROM (
SELECT *, f_unnest_ord_idx(arr) AS rec
FROM (VALUES (1, '{a,b,c}'::text[]) -- short for: '[1:3]={a,b,c}'
, (2, '[5:7]={a,b,c}')
, (3, '[-9:-7]={a,b,c}')
) t(id, arr)
) sub;
id | arr | val | ordinality | idx
----+-----------------+-----+------------+-----
1 | {a,b,c} | a | 1 | 1
1 | {a,b,c} | b | 2 | 2
1 | {a,b,c} | c | 3 | 3
2 | [5:7]={a,b,c} | a | 1 | 5
2 | [5:7]={a,b,c} | b | 2 | 6
2 | [5:7]={a,b,c} | c | 3 | 7
3 | [-9:-7]={a,b,c} | a | 1 | -9
3 | [-9:-7]={a,b,c} | b | 2 | -8
3 | [-9:-7]={a,b,c} | c | 3 | -7
So sánh:
- Chuẩn hóa các chỉ số con của mảng cho mảng 1 chiều để chúng bắt đầu bằng 1