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ột
bigint
cột được nối vào đầu ra bắt đầu từ 1 và tăng lên 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 biểu mẫu tương đương, ít dài dòng hơn với hàm ý CROSS JOIN LATERAL
:
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ột 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í danh của bảng và 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) để thêm bí danh cột rõ ràng và các cột đủ tiêu chuẩn bảng.
Postgres 8,4 - 9,3
Với việc row_number() OVER (PARTITION BY id ORDER BY elem)
bạn lấy 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ó xảy ra để làm việc do một chi tiết triển khai.
Để đảm bảo số thứ tự của 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ột 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:
Postgres 8.1 - 8.4
Không ai trong số những tính năng có sẵn, tuy nhiên: 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[])
, (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: