Postgres 9.4 atau lebih baru
Gunakan WITH ORDINALITY
untuk fungsi set-return:
Ketika sebuah fungsi di
FROM
klausa diakhiri denganWITH ORDINALITY
, sebuahbigint
kolom ditambahkan ke output yang dimulai dari 1 dan bertambah 1 untuk setiap baris output fungsi. Ini sangat berguna dalam kasus set kembali fungsi sepertiunnest()
.
Dikombinasikan dengan LATERAL
fitur di pg 9.3+, dan menurut utas ini di pgsql-hackers, kueri di atas sekarang dapat ditulis sebagai:
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
mempertahankan semua baris di tabel kiri, bahkan jika ekspresi tabel di sebelah kanan tidak mengembalikan baris. Jika itu bukan masalah, Anda dapat menggunakan ini jika tidak setara, kurang bertele-tele formulir dengan CROSS JOIN LATERAL
implicit implisit :
SELECT t.id, a.elem, a.nr
FROM tbl t, unnest(string_to_array(t.elements, ',')) WITH ORDINALITY a(elem, nr);
Atau lebih sederhana jika didasarkan pada array aktual (arr
menjadi kolom array):
SELECT t.id, a.elem, a.nr
FROM tbl t, unnest(t.arr) WITH ORDINALITY a(elem, nr);
Atau bahkan, dengan sintaks minimal:
SELECT id, a, ordinality
FROM tbl, unnest(arr) WITH ORDINALITY a;
a
secara otomatis tabel dan alias kolom. Nama default dari kolom ordinalitas yang ditambahkan adalah ordinality
. Tetapi lebih baik (lebih aman, lebih bersih) untuk menambahkan alias kolom eksplisit dan kolom yang memenuhi syarat tabel.
Postgres 8.4 - 9.3
Dengan row_number() OVER (PARTITION BY id ORDER BY elem)
Anda mendapatkan nomor sesuai dengan urutannya, bukan nomor urut dari posisi ordinal asli dalam string.
Anda cukup menghilangkan ORDER BY
:
SELECT *, row_number() OVER (PARTITION by id) AS nr
FROM (SELECT id, regexp_split_to_table(elements, ',') AS elem FROM tbl) t;
Meskipun ini biasanya berfungsi dan saya belum pernah melihatnya gagal dalam kueri sederhana, PostgreSQL tidak menegaskan apa pun tentang urutan baris tanpa ORDER BY
. Itu berhasil karena detail implementasi.
Untuk menjamin nomor urut elemen dalam string . yang dipisahkan kosong :
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;
Atau lebih sederhana jika didasarkan pada array aktual :
SELECT id, arr[nr] AS elem, nr
FROM (SELECT *, generate_subscripts(arr, 1) AS nr FROM tbl) t;
Jawaban terkait di dba.SE:
- Bagaimana cara mempertahankan urutan asli elemen dalam larik tak bersarang?
Postgres 8.1 - 8.4
Belum ada satu pun dari fitur ini yang tersedia:RETURNS TABLE
, generate_subscripts()
, unnest()
, array_length()
. Tapi ini berhasil:
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';
Perhatikan khususnya, bahwa indeks array dapat berbeda dari posisi ordinal elemen. Pertimbangkan demo ini dengan fungsi yang diperluas :
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
Bandingkan:
- Menormalkan subskrip larik untuk larik 1 dimensi sehingga dimulai dengan 1