. row_number())
Tôi có trường hợp sử dụng tương tự. Đối với mỗi bản ghi chèn vào một dự án cụ thể trong SaaS, chúng ta cần có một độc đáo, incrementing số có thể được tạo ra khi đối mặt với đồng thời INSERTs và là lý tưởng Gapless.
Bài viết này mô tả một giải pháp hay , mà tôi sẽ tóm tắt ở đây để dễ dàng và cho hậu thế.
- Có một bảng riêng đóng vai trò là bộ đếm để cung cấp giá trị tiếp theo. Nó sẽ có hai cột,
document_idvà counter. countersẽ là DEFAULT 0Cách khác, nếu bạn đã có một documentthực thể nhóm tất cả các phiên bản, một countercó thể được thêm vào đó.
- Thêm một
BEFORE INSERTkích hoạt vào document_versionsbảng mà nguyên tử tăng bộ đếm ( UPDATE document_revision_counters SET counter = counter + 1 WHERE document_id = ? RETURNING counter) và sau đó đặt NEW.versionthành giá trị bộ đếm đó.
Ngoài ra, bạn có thể sử dụng CTE để thực hiện việc này ở lớp ứng dụng (mặc dù tôi thích nó là một trình kích hoạt cho mục đích nhất quán):
WITH version AS (
UPDATE document_revision_counters
SET counter = counter + 1
WHERE document_id = 1
RETURNING counter
)
INSERT
INTO document_revisions (document_id, rev, other_data)
SELECT 1, version.counter, 'some other data'
FROM "version";
Về nguyên tắc, điều này tương tự như cách bạn đã cố gắng giải quyết nó ban đầu, ngoại trừ việc sửa đổi một hàng truy cập trong một câu lệnh duy nhất, nó chặn các giá trị cũ cho đến khi INSERTcam kết.
Đây là một bản ghi từ psqlcho thấy điều này trong hành động:
scratch=# CREATE TABLE document_revisions (document_id integer, rev integer, other_data text, PRIMARY KEY (document_id, rev));
CREATE TABLE
scratch=# CREATE TABLE document_revision_counters (document_id integer PRIMARY KEY, counter integer DEFAULT 0);
CREATE TABLE
scratch=# WITH version AS (
INSERT INTO document_revision_counters (document_id) VALUES (2)
ON CONFLICT (document_id)
DO UPDATE SET counter = document_revision_counters.counter + 1
RETURNING counter;
)
INSERT
INTO document_revisions (document_id, rev, other_data)
SELECT 2, version.counter, 'doc 1 v1'
FROM "version";
INSERT 0 1
scratch=# WITH version AS (
INSERT INTO document_revision_counters (document_id) VALUES (2)
ON CONFLICT (document_id)
DO UPDATE SET counter = document_revision_counters.counter + 1
RETURNING counter;
)
INSERT
INTO document_revisions (document_id, rev, other_data)
SELECT 2, version.counter, 'doc 1 v2'
FROM "version";
INSERT 0 1
scratch=# WITH version AS (
INSERT INTO document_revision_counters (document_id) VALUES (2)
ON CONFLICT (document_id)
DO UPDATE SET counter = document_revision_counters.counter + 1
RETURNING counter;
)
INSERT
INTO document_revisions (document_id, rev, other_data)
SELECT 2, version.counter, 'doc 2 v1'
FROM "version";
INSERT 0 1
scratch=# SELECT * FROM document_revisions;
document_id | rev | other_data
-------------+-----+------------
2 | 1 | doc 1 v1
2 | 2 | doc 1 v2
2 | 1 | doc 2 v1
(3 rows)
Như bạn có thể thấy, bạn phải cẩn thận về cách INSERTxảy ra, do đó phiên bản kích hoạt, trông như thế này:
CREATE OR REPLACE FUNCTION set_doc_revision()
RETURNS TRIGGER AS $$ BEGIN
WITH version AS (
INSERT INTO document_revision_counters (document_id, counter) VALUES (NEW.document_id, 1)
ON CONFLICT (document_id)
DO UPDATE SET counter = document_revision_counters.counter + 1
RETURNING counter
)
SELECT INTO NEW.rev counter FROM version; RETURN NEW; END;
$$ LANGUAGE 'plpgsql';
CREATE TRIGGER set_doc_revision BEFORE INSERT ON document_revisions
FOR EACH ROW EXECUTE PROCEDURE set_doc_revision();
Điều đó làm cho INSERTs thẳng hơn nhiều và tính toàn vẹn của dữ liệu mạnh hơn khi đối mặt với INSERTnguồn gốc từ các nguồn tùy ý:
scratch=# INSERT INTO document_revisions (document_id, other_data) VALUES (1, 'baz');
INSERT 0 1
scratch=# INSERT INTO document_revisions (document_id, other_data) VALUES (1, 'foo');
INSERT 0 1
scratch=# INSERT INTO document_revisions (document_id, other_data) VALUES (1, 'bar');
INSERT 0 1
scratch=# INSERT INTO document_revisions (document_id, other_data) VALUES (42, 'meaning of life');
INSERT 0 1
scratch=# SELECT * FROM document_revisions;
document_id | rev | other_data
-------------+-----+-----------------
1 | 1 | baz
1 | 2 | foo
1 | 3 | bar
42 | 1 | meaning of life
(4 rows)