Postgres 9.4 atau lebih baru
Gunakan WITH ORDINALITY
untuk fungsi set-return:
Jika fungsi dalam FROM
klausa diakhiri dengan WITH ORDINALITY
,
bigint
kolom ditambahkan ke keluaran yang dimulai dari 1 dan bertambah 1 untuk setiap baris keluaran fungsi. Ini paling berguna dalam kasus set mengembalikan fungsi seperti unnest()
.
Dalam kombinasi 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, meskipun ekspresi tabel di kanan tidak menghasilkan baris. Jika itu bukan urusan Anda, Anda dapat menggunakan bentuk yang setara ini, lebih sedikit verbose dengan implisit CROSS JOIN LATERAL
:
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 larik sebenarnya ( arr
berupa kolom larik):
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 menjadi alias tabel dan kolom. Nama default dari kolom ordinalitas yang ditambahkan adalah ordinality
. Tapi lebih baik (lebih aman, lebih bersih) menambahkan alias kolom eksplisit dan kolom kualifikasi tabel.
Postgres 8.4 - 9.3
Dengan row_number() OVER (PARTITION BY id ORDER BY elem)
Anda mendapatkan nomor sesuai dengan urutan urutan, bukan nomor urut dari posisi urutan 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 tidak pernah melihatnya gagal dalam kueri sederhana, PostgreSQL tidak menegaskan apa pun tentang urutan baris tanpa ORDER BY
. Itu terjadi untuk bekerja 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 larik sebenarnya :
SELECT id, arr[nr] AS elem, nr
FROM (SELECT *, generate_subscripts(arr, 1) AS nr FROM tbl) t;
Jawaban terkait di dba.SE:
Postgres 8.1 - 8.4
Tak satu pun dari fitur ini tersedia, namun: 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 secara khusus, bahwa indeks larik dapat berbeda dari posisi ordinal elemen. Pertimbangkan demo ini dengan fungsi tambahan :
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
Membandingkan: