(이 주제에 관한 기사를 다시 발견하려고 할 때이 질문에 왔습니다. 이제 그것을 찾았으므로 다른 사람들이 현재 선택된 답변에 대한 대체 옵션을 추구하는 경우 여기에 게시하고 있습니다. row_number()
)
이 같은 사용 사례가 있습니다. 우리는 동시의 얼굴에 생성 할 수있는 고유의 증가 수를 필요로 우리의 SaaS의 특정 프로젝트에 삽입 된 각 레코드 INSERT
의이고 이상적으로 끊김없는합니다.
이 기사에서는 훌륭한 솔루션에 대해 설명합니다 .
- 다음 값을 제공하기 위해 카운터 역할을하는 별도의 테이블이 있습니다. 그것은 두 개의 열을 가지고 것입니다
document_id
및 counter
. counter
될 것입니다 DEFAULT 0
당신이 이미있는 경우, 또는 document
그룹의 모든 버전하는 그 실체 counter
가 추가 될 수 있습니다.
- 카운터 ( ) 를 원자 적으로 증가시킨 다음 해당 카운터 값으로 설정 하는
BEFORE INSERT
트리거를 document_versions
테이블에 추가하십시오 .UPDATE document_revision_counters SET counter = counter + 1 WHERE document_id = ? RETURNING counter
NEW.version
또는 CTE를 사용하여 응용 프로그램 계층 에서이 작업을 수행 할 수 있습니다 (일관성을 위해 트리거로 선호하지만).
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";
이것은 단일 명령문에서 카운터 행을 수정하여이 값 INSERT
이 커밋 될 때까지 부실 값의 읽기를 차단한다는 점을 제외하고는 처음에이를 해결하려는 방법과 유사 합니다.
다음은 psql
이를 실제로 보여주는 내용입니다.
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)
보시다시피, INSERT
발생 방식에주의해야 하므로 트리거 버전은 다음과 같습니다.
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();
이는 임의의 소스에서 비롯된 INSERT
데이터에 비해 훨씬 더 직설적이고 데이터의 무결성을 강화 INSERT
합니다.
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)