Trình tự có khoảng trống để cho phép chèn đồng thời. Cố gắng tránh khoảng trống hoặc sử dụng lại ID đã xóa sẽ tạo ra các vấn đề hiệu suất khủng khiếp. Xem PostgreSQL wiki FAQ.
PostgreSQL SEQUENCE
s được sử dụng để phân bổ ID. Những điều này chỉ tăng lên, và chúng được miễn quy tắc rollback giao dịch thông thường để cho phép nhiều giao dịch lấy các ID mới cùng một lúc. Điều này có nghĩa là nếu một giao dịch quay lại, những ID đó sẽ bị "vứt bỏ"; không có danh sách các ID "miễn phí" được lưu giữ, chỉ là bộ đếm ID hiện tại. Trình tự cũng thường được tăng lên nếu cơ sở dữ liệu tắt không rõ ràng.
Khóa tổng hợp (ID) là vô nghĩa. Thứ tự của họ không đáng kể, tài sản duy nhất của họ về ý nghĩa là duy nhất. Bạn không thể đo lường một cách có ý nghĩa cách hai "xa nhau" hai ID, và bạn cũng không thể nói một cách có ý nghĩa nếu một ID lớn hơn hoặc nhỏ hơn một ID khác. Tất cả những gì bạn có thể làm là nói "bằng" hoặc "không bằng nhau". Bất cứ điều gì khác là không an toàn. Bạn không nên quan tâm đến những khoảng trống.
Nếu bạn cần một chuỗi không có khoảng cách sử dụng lại ID đã xóa, bạn có thể có một ID, bạn chỉ cần từ bỏ một lượng lớn hiệu suất cho nó - cụ thể là bạn không thể có bất kỳ đồng thời nào trên INSERT
s vì bạn phải quét bảng để có ID miễn phí thấp nhất, hãy khóa bảng để viết để không có giao dịch nào khác có thể yêu cầu cùng một ID. Hãy thử tìm kiếm "chuỗi không có khoảng cách postgresql".
Cách tiếp cận đơn giản nhất là sử dụng bảng truy cập và hàm nhận ID tiếp theo. Đây là một phiên bản tổng quát sử dụng một bảng truy cập để tạo các ID không có khoảng cách liên tiếp; tuy nhiên, nó không sử dụng lại ID.
CREATE TABLE thetable_id_counter (last_id integer not null);
INSERT INTO thetable_id_counter VALUES (0);
CREATE OR REPLACE FUNCTION get_next_id(countertable regclass, countercolumn text) RETURNS integer AS $$
DECLARE
next_value integer;
BEGIN
EXECUTE format('UPDATE %s SET %I = %I + 1 RETURNING %I', countertable, countercolumn, countercolumn, countercolumn) INTO next_value;
RETURN next_value;
END;
$$ LANGUAGE plpgsql;
COMMENT ON get_next_id(countername regclass) IS 'Increment and return value from integer column $2 in table $1';
Cách sử dụng:
INSERT INTO dummy(id, blah)
VALUES (get_next_id('thetable_id_counter','last_id'), 42);
Lưu ý rằng khi một giao dịch mở đã thu được một ID, tất cả các giao dịch khác mà cố gắng gọi get_next_id
sẽ chặn cho đến khi giao dịch 1 cam kết hoặc cuộn lại. Điều này là không thể tránh khỏi và cho ID không có khoảng cách và được thiết kế.
Nếu bạn muốn lưu trữ nhiều bộ đếm cho các mục đích khác nhau trong bảng, chỉ cần thêm tham số vào hàm trên, thêm cột vào bảng truy cập và thêm mệnh đề WHERE
vào UPDATE
khớp với thông số được thêm vào cột. Bằng cách đó bạn có thể có nhiều hàng truy cập bị khóa độc lập. Do không chỉ cần thêm cột bổ sung cho các bộ đếm mới.
Chức năng này không sử dụng lại ID đã xóa, nó chỉ tránh giới thiệu khoảng trống.
Để sử dụng lại ID tôi khuyên ... không sử dụng lại ID. Nếu bạn thực sự phải, bạn có thể làm như vậy bằng cách thêm một kích hoạt ON INSERT OR UPDATE OR DELETE
trên bàn quan tâm thêm ID đã xóa vào bảng bên cạnh danh sách miễn phí và xóa chúng khỏi bảng danh sách miễn phí khi chúng là INSERT
ed . Hãy xử lý một số UPDATE
dưới dạng DELETE
theo sau là INSERT
.Bây giờ hãy sửa đổi chức năng tạo ID ở trên để nó hoạt động SELECT free_id INTO next_value FROM free_ids FOR UPDATE LIMIT 1
và nếu được tìm thấy, DELETE
s hàng đó. IF NOT FOUND
nhận ID mới từ bảng máy phát điện như bình thường. Đây là tiện ích mở rộng chưa được kiểm tra của hàm trước để hỗ trợ sử dụng lại:
CREATE OR REPLACE FUNCTION get_next_id_reuse(countertable regclass, countercolumn text, freelisttable regclass, freelistcolumn text) RETURNS integer AS $$
DECLARE
next_value integer;
BEGIN
EXECUTE format('SELECT %I FROM %s FOR UPDATE LIMIT 1', freelistcolumn, freelisttable) INTO next_value;
IF next_value IS NOT NULL THEN
EXECUTE format('DELETE FROM %s WHERE %I = %L', freelisttable, freelistcolumn, next_value);
ELSE
EXECUTE format('UPDATE %s SET %I = %I + 1 RETURNING %I', countertable, countercolumn, countercolumn, countercolumn) INTO next_value;
END IF;
RETURN next_value;
END;
$$ LANGUAGE plpgsql;
MySQL không nên sử dụng lại ID tự động, trừ khi bạn xóa ID cao nhất. – ceejayoz
Ah! cảm ơn, đó là đúng. OK - Tôi có thể sống với điều đó :) – fatfrog
Bạn cũng không nên quan tâm đến các ID. Họ chỉ là những con số vô nghĩa. –