. 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 INSERT
s 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_id
và counter
. counter
sẽ là DEFAULT 0
Cách khác, nếu bạn đã có một document
thực thể nhóm tất cả các phiên bản, một counter
có thể được thêm vào đó.
- Thêm một
BEFORE INSERT
kích hoạt vào document_versions
bả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.version
thà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 INSERT
cam kết.
Đây là một bản ghi từ psql
cho 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 INSERT
xả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 INSERT
s 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 INSERT
nguồ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)