Trước hết, khoảng trống trong một chuỗi sẽ được dự kiến. Tự hỏi nếu bạn thực sự cần phải loại bỏ chúng. Cuộc sống của bạn trở nên đơn giản hơn nếu bạn chỉ sống với nó. Để có được các số không có khoảng cách, phương án (thường tốt hơn) là sử dụng a VIEW
với row_number()
. Ví dụ trong câu trả lời liên quan này:
Dưới đây là một số công thức để loại bỏ khoảng cách.
1. Bảng mới, nguyên sơ
Tránh các biến chứng với các vi phạm duy nhất và phình bảng và nhanh chóng . Chỉ dành cho các trường hợp đơn giản mà bạn không bị ràng buộc bởi các tham chiếu FK, các khung nhìn trên bảng hoặc các đối tượng tùy thuộc khác hoặc bởi truy cập đồng thời. Thực hiện trong một giao dịch để tránh tai nạn:
BEGIN;
LOCK tbl;
CREATE TABLE tbl_new (LIKE tbl INCLUDING ALL);
INSERT INTO tbl_new -- no target list in this case
SELECT row_number() OVER (ORDER BY id), data -- all columns in default order
FROM tbl;
ALTER SEQUENCE tbl_id_seq OWNED BY tbl_new.id; -- make new table own sequence
DROP TABLE tbl;
ALTER TABLE tbl_new RENAME TO tbl;
SELECT setval('tbl_id_seq', max(id)) FROM tbl; -- reset sequence
COMMIT;
CREATE TABLE tbl_new (LIKE tbl INCLUDING ALL)
sao chép cấu trúc bao gồm. các ràng buộc và mặc định từ bảng gốc. Sau đó, tạo cột bảng mới sở hữu chuỗi:
Và đặt lại về mức tối đa mới:
Điều này mang lại lợi thế là bảng mới không bị phình to và co cụm id
.
2. UPDATE
tại chỗ
Điều này tạo ra rất nhiều hàng chết và yêu cầu (tự động) VACUUM
sau này.
Nếu serial
cột cũng là PRIMARY KEY
(như trong trường hợp của bạn) hoặc có một UNIQUE
ràng buộc, bạn phải tránh các vi phạm duy nhất trong quy trình. Mặc định (rẻ hơn) cho các ràng buộc PK / UNIQUE là NOT DEFERRABLE
, điều này buộc kiểm tra sau mỗi hàng đơn. Tất cả các chi tiết theo câu hỏi liên quan này trên SO:
Bạn có thể định nghĩa ràng buộc của mình là DEFERRABLE
(điều này làm cho nó đắt hơn).
Hoặc bạn có thể bỏ các ràng buộc và thêm lại khi bạn hoàn thành:
BEGIN;
LOCK tbl;
ALTER TABLE tbl DROP CONSTRAINT tbl_pkey; -- remove PK
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;
SELECT setval('tbl_id_seq', max(id)) FROM tbl; -- reset sequence
ALTER TABLE tbl ADD CONSTRAINT tbl_pkey PRIMARY KEY(id); -- add PK back
COMMIT;
Không thể trong khi bạn có cácFOREIGN KEY
ràng buộc tham chiếu (các) cột vì ( trên mỗi tài liệu ):
Các cột được tham chiếu phải là các cột của ràng buộc khóa chính hoặc duy nhất không thể bảo vệ trong bảng được tham chiếu.
Bạn sẽ cần (khóa tất cả các bảng có liên quan và) thả / tạo lại các ràng buộc FK và cập nhật tất cả các giá trị FK theo cách thủ công (xem tùy chọn 3. ). Hoặc bạn phải di chuyển các giá trị ra khỏi đường trong một giây UPDATE
để tránh xung đột. Chẳng hạn, giả sử bạn không có số âm:
BEGIN;
LOCK tbl;
UPDATE tbl SET id = id * -1; -- avoid conflicts
UPDATE tbl t
SET id = t1.new_id
FROM (SELECT id, row_number() OVER (ORDER BY id DESC) AS new_id FROM tbl) t1
WHERE t.id = t1.id;
SELECT setval('tbl_id_seq', max(id)) FROM tbl; -- reset sequence
COMMIT;
Hạn chế như đã đề cập ở trên.
3. Bảng tạm thời TRUNCATE
,,INSERT
Thêm một lựa chọn nếu bạn có nhiều RAM. Điều này kết hợp một số lợi thế của hai cách đầu tiên. Gần như nhanh như tùy chọn 1. và bạn có được một bảng mới, nguyên sơ không phình to nhưng vẫn giữ tất cả các ràng buộc và phụ thuộc như trong tùy chọn 2.
Tuy nhiên , theo mỗi tài liệu:
TRUNCATE
không thể được sử dụng trên một bảng có tham chiếu khóa ngoài
từ các bảng khác, trừ khi tất cả các bảng như vậy cũng bị cắt ngắn trong cùng một lệnh. Kiểm tra tính hợp lệ trong các trường hợp như vậy sẽ yêu cầu quét bảng và toàn bộ điểm không được thực hiện.
Nhấn mạnh đậm của tôi.
Bạn có thể tạm thời bỏ các ràng buộc FK và sử dụng các CTE sửa đổi dữ liệu để cập nhật tất cả các cột FK:
SET temp_buffers = 500MB; -- example value, see 1st link below
BEGIN;
CREATE TEMP TABLE tbl_tmp AS
SELECT row_number() OVER (ORDER BY id) AS new_id, *
FROM tbl
ORDER BY id; -- order here to use index (if one exists)
-- drop FK constraints in other tables referencing this one
-- which takes out an exclusive lock on those tables
TRUNCATE tbl;
INSERT INTO tbl
SELECT new_id, data -- list all columns in order
FROM tbl_tmp; -- rely on established order in tbl_tmp
-- ORDER BY id; -- only to be absolutely sure (not necessary)
-- example for table "fk_tbl" with FK column "fk_id"
UPDATE fk_tbl f
SET fk_id = t.new_id -- set to new ID
FROM tbl_tmp t
WHERE f.fk_id = t.id; -- match on old ID
-- add FK constraints in other tables back
COMMIT;
Liên quan, với nhiều chi tiết hơn:
FOREIGN KEYS
được đặt thànhCASCADE
không thể bạn chỉ cần lặp qua các khóa chính cũ và cập nhật giá trị của chúng tại chỗ (từ giá trị cũ sang giá trị mới)? Về cơ bản, đây là tùy chọn 3 mà không cầnTRUNCATE tbl
thay thếINSERT
bằng mộtUPDATE
và không cần cập nhật khóa ngoại theo cách thủ công.