Biasanya desain yang buruk untuk menyimpan nilai CSV dalam satu kolom. Jika memungkinkan, gunakan array atau desain yang dinormalisasi dengan benar.
Sementara terjebak dengan situasi Anda saat ini ...
Untuk jumlah maksimum elemen kecil yang diketahui
Solusi sederhana tanpa tipu daya atau rekursi akan berhasil:
SELECT id, 1 AS rnk
, split_part(csv, ', ', 1) AS c1
, split_part(csv, ', ', 2) AS c2
, split_part(csv, ', ', 3) AS c3
, split_part(csv, ', ', 4) AS c4
, split_part(csv, ', ', 5) AS c5
FROM tbl
WHERE split_part(csv, ', ', 1) <> '' -- skip empty rows
UNION ALL
SELECT id, 2
, split_part(csv, ', ', 6)
, split_part(csv, ', ', 7)
, split_part(csv, ', ', 8)
, split_part(csv, ', ', 9)
, split_part(csv, ', ', 10)
FROM tbl
WHERE split_part(csv, ', ', 6) <> '' -- skip empty rows
-- three more blocks to cover a maximum "around 20"
ORDER BY id, rnk;
db<>fiddle di sini
id
menjadi PK dari tabel asli.
Ini mengasumsikan ', ' sebagai pemisah, tentu saja.
Anda dapat beradaptasi dengan mudah.
Terkait:
Untuk jumlah elemen yang tidak diketahui
Berbagai cara. Sekali jalan gunakan regexp_replace()
untuk mengganti setiap pemisah kelima sebelum membuka sarang ...
-- for any number of elements
SELECT t.id, c.rnk
, split_part(c.csv5, ', ', 1) AS c1
, split_part(c.csv5, ', ', 2) AS c2
, split_part(c.csv5, ', ', 3) AS c3
, split_part(c.csv5, ', ', 4) AS c4
, split_part(c.csv5, ', ', 5) AS c5
FROM tbl t
, unnest(string_to_array(regexp_replace(csv, '((?:.*?,){4}.*?),', '\1;', 'g'), '; ')) WITH ORDINALITY c(csv5, rnk)
ORDER BY t.id, c.rnk;
db<>fiddle di sini
Ini mengasumsikan bahwa pemisah yang dipilih ;
tidak pernah muncul di string Anda. (Sama seperti ,
tidak akan pernah muncul.)
Pola ekspresi reguler adalah kuncinya:'((?:.*?,){4}.*?),'
(?:)
... kumpulan tanda kurung “non-capturing”
()
... kumpulan tanda kurung “menangkap” *?
... pengukur tidak serakah
{4}?
... urutan tepat 4 kecocokan
Pengganti '\1;'
berisi referensi balik
\1
.
'g'
karena parameter fungsi keempat diperlukan untuk penggantian berulang.
Bacaan lebih lanjut:
- PostgreSQL ®exp_split_to_array + unnest
- Terapkan ` trim()` dan `regexp_replace()` pada larik teks
- PostgreSQL unnest() dengan nomor elemen
Cara lain untuk menyelesaikan ini termasuk CTE rekursif atau fungsi set-return ...
Isi dari kanan ke kiri
(Seperti yang Anda tambahkan di Bagaimana cara memasukkan nilai mulai dari sisi kanan ke dalam kolom?
)
Cukup hitung mundur angka seperti:
SELECT t.id, c.rnk
, split_part(c.csv5, ', ', 5) AS c1
, split_part(c.csv5, ', ', 4) AS c2
, split_part(c.csv5, ', ', 3) AS c3
, split_part(c.csv5, ', ', 2) AS c4
, split_part(c.csv5, ', ', 1) AS c5
FROM ...
db<>fiddle di sini