Pertanyaannya sudah lama, tetapi kami mendapat pertanyaan baru dari pengguna yang putus asa di dba.SE setelah mencoba menerapkan apa yang disarankan di sini. Temukan jawaban dengan detail dan penjelasan lebih lanjut di sana :
jawaban yang diterima saat ini untuk sebagian besar kasus akan gagal .
-
Biasanya, Anda memiliki
PRIMARY KEY
atauUNIQUE
batasan padaid
kolom, yangNOT DEFERRABLE
secara default. (OP menyebutkanreferences and constraints
.) Batasan tersebut diperiksa setelah setiap baris, sehingga kemungkinan besar Anda mendapatkan pelanggaran unik kesalahan mencoba. Detail: -
Biasanya, seseorang ingin mempertahankan urutan baris asli sambil menutup celah. Tetapi urutan pembaruan baris adalah sewenang-wenang , mengarah ke angka arbitrer. Contoh yang didemonstrasikan tampaknya mempertahankan urutan aslinya karena penyimpanan fisik masih sesuai dengan urutan yang diinginkan (baris dimasukkan dalam urutan yang diinginkan sesaat sebelumnya), yang hampir tidak pernah terjadi dalam aplikasi dunia nyata dan sama sekali tidak dapat diandalkan.
Masalahnya lebih rumit daripada yang terlihat pada awalnya. Satu solusi (antara lain) jika Anda mampu menghapus batasan PK / UNIK (dan batasan FK terkait) untuk sementara:
BEGIN;
LOCK tbl;
-- remove all FK constraints to the column
ALTER TABLE tbl DROP CONSTRAINT tbl_pkey; -- remove PK
-- for the simple case without FK references - or see below:
UPDATE tbl t -- intermediate unique violations are ignored now
SET id = t1.new_id
FROM (SELECT id, row_number() OVER (ORDER BY id) AS new_id FROM tbl) t1
WHERE t.id = t1.id;
-- Update referencing value in FK columns at the same time (if any)
SELECT setval('tbl_id_seq', max(id)) FROM tbl; -- reset sequence
ALTER TABLE tbl ADD CONSTRAINT tbl_pkey PRIMARY KEY(id); -- add PK back
-- add all FK constraints to the column back
COMMIT;
Ini juga banyak lebih cepat untuk tabel besar, karena memeriksa batasan PK (dan FK) untuk setiap baris lebih mahal daripada menghapus batasan dan menambahkannya kembali.
Jika ada kolom FK di tabel lain yang merujuk ke tbl.id
, gunakan CTE pengubah data untuk memperbarui semuanya.
Contoh untuk tabel fk_tbl
dan kolom FK fk_id
:
WITH u1 AS (
UPDATE tbl t
SET id = t1.new_id
FROM (SELECT id, row_number() OVER (ORDER BY id) AS new_id FROM tbl) t1
WHERE t.id = t1.id
RETURNING t.id, t1.new_id -- return old and new ID
)
UPDATE fk_tbl f
SET fk_id = u1.new_id -- set to new ID
FROM u1
WHERE f.fk_id = u1.id; -- match on old ID
Selengkapnya di jawaban yang dirujuk di dba.SE .