Câu hỏi cũ, nhưng chúng tôi nhận được một câu hỏi mới từ một người dùng tuyệt vọng trên dba.SE sau khi cố gắng áp dụng những gì được đề xuất ở đây. Tìm câu trả lời với thêm chi tiết và giải thích ở đó :
câu trả lời hiện được chấp nhận sẽ không thành công trong hầu hết các trường hợp .
-
Thông thường, bạn có
PRIMARY KEY
hoặcUNIQUE
ràng buộc trên mộtid
cộtNOT DEFERRABLE
theo mặc định. (OP đề cập đếnreferences and constraints
.) Những ràng buộc như vậy được kiểm tra sau mỗi hàng, vì vậy bạn rất có thể gặp vi phạm duy nhất lỗi khi cố gắng. Chi tiết: -
Thông thường, một người muốn giữ lại thứ tự hàng ban đầu đồng thời thu hẹp khoảng cách. Nhưng thứ tự các hàng được cập nhật là tùy ý , dẫn đến số lượng tùy ý. Ví dụ được minh họa dường như vẫn giữ nguyên trình tự ban đầu vì bộ nhớ vật lý vẫn trùng với thứ tự mong muốn (các hàng được chèn theo thứ tự mong muốn chỉ một lúc trước đó), điều này hầu như không bao giờ xảy ra trong các ứng dụng trong thế giới thực và hoàn toàn không đáng tin cậy.
Vấn đề phức tạp hơn tưởng tượng lúc đầu. Một giải pháp (trong số những giải pháp khác) nếu bạn có đủ khả năng để tạm thời loại bỏ ràng buộc PK / UNIQUE (và các ràng buộc FK liên quan):
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;
Điều này cũng nhiều nhanh hơn đối với các bảng lớn, bởi vì việc kiểm tra (các) ràng buộc PK (và FK) cho mọi hàng tốn kém hơn rất nhiều so với việc loại bỏ (các) ràng buộc và thêm lại (chúng).
Nếu có các cột FK trong các bảng khác tham chiếu đến tbl.id
, sử dụng CTE sửa đổi dữ liệu để cập nhật tất cả chúng.
Ví dụ cho bảng fk_tbl
và một cột 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
Xem thêm trong câu trả lời được tham khảo trên dba.SE .