9.5 이상 :
PostgreSQL 9.5 및 최신 지원 INSERT ... ON CONFLICT UPDATE
(및 ON CONFLICT DO NOTHING
), 즉 upsert.
와 비교ON DUPLICATE KEY UPDATE
.
빠른 설명 .
사용을위한 참조 매뉴얼 구체적 - conflict_action의 신택스 다이어그램 절 및 해설 텍스트 .
아래에 제공된 9.4 및 이전 버전의 솔루션과 달리이 기능은 여러 개의 충돌하는 행에서 작동하며 독점 잠금 또는 재시도 루프가 필요하지 않습니다.
는 기능을 추가하는 것은 여기에 투입 하고 개발 중심으로 논의가 여기에있다 .
9.5에 있고 이전 버전과 호환되지 않아도되는 경우 지금 읽기를 중지 할 수 있습니다 .
9.4 이상 :
PostgreSQL에는 내장 UPSERT
(또는 MERGE
) 기능 이 없으므로 동시 사용시 효율적으로 수행하는 것은 매우 어렵습니다.
이 기사는 문제를 유용한 자세하게 설명 합니다.
일반적으로 두 가지 옵션 중에서 선택해야합니다.
- 재시도 루프의 개별 삽입 / 업데이트 작업 또는
- 테이블 잠금 및 일괄 병합
개별 행 재시도 루프
많은 연결이 동시에 삽입을 시도하려는 경우 재시도 루프에서 개별 행 업 서트를 사용하는 것이 적합한 옵션입니다.
PostgreSQL 문서에는 데이터베이스 내부 루프에서이를 수행 할 수있는 유용한 절차가 포함되어 있습니다 . 대부분의 순진한 솔루션과 달리 업데이트 손실 및 삽입 레이스를 방지합니다. 그것은 오직 READ COMMITTED
모드 에서만 작동하며 그것이 당신이 거래에서하는 유일한 일이라면 안전합니다. 트리거 또는 보조 고유 키로 인해 고유 한 위반이 발생하면이 기능이 제대로 작동하지 않습니다.
이 전략은 매우 비효율적입니다. 실용적 일 때마다 작업을 대기시키고 대신 아래 설명 된대로 대량 업 사트를 수행해야합니다.
이 문제에 대한 많은 시도 된 솔루션은 롤백을 고려하지 않으므로 업데이트가 불완전합니다. 두 거래는 서로 경쟁합니다. 그들 중 하나는 성공적으로 INSERT
s; 다른 하나는 중복 키 오류를 가져오고 UPDATE
대신 수행합니다. UPDATE
(가)에 대한 블록 기다리고 INSERT
롤백 또는 커밋. 롤백 할 때 UPDATE
조건 재확인은 0 행과 일치하므로 UPDATE
커밋이 실제로 예상 한 upsert를 수행하지는 않았습니다. 결과 행 수를 확인하고 필요한 경우 다시 시도해야합니다.
일부 시도 된 솔루션은 SELECT 레이스도 고려하지 않습니다. 당신이 명백하고 간단한 것을 시도한다면 :
-- THIS IS WRONG. DO NOT COPY IT. It's an EXAMPLE.
BEGIN;
UPDATE testtable
SET somedata = 'blah'
WHERE id = 2;
-- Remember, this is WRONG. Do NOT COPY IT.
INSERT INTO testtable (id, somedata)
SELECT 2, 'blah'
WHERE NOT EXISTS (SELECT 1 FROM testtable WHERE testtable.id = 2);
COMMIT;
두 번에 두 번 실행되면 몇 가지 실패 모드가 있습니다. 하나는 업데이트 재확인과 관련하여 이미 논의 된 문제입니다. 다른 하나는 UPDATE
동시에 0 행과 일치하고 계속되는 곳입니다. 그리고 둘은 할 EXISTS
일이 시험 전에 을 INSERT
. 둘 다 0 행을 얻으므로 둘 다 수행합니다 INSERT
. 중복 키 오류로 실패합니다.
이것이 재시도 루프가 필요한 이유입니다. 현명한 SQL로 중복 키 오류 또는 업데이트 손실을 방지 할 수 있다고 생각할 수는 있지만 그렇게 할 수는 없습니다. 선택한 개수에 따라 행 개수를 확인하거나 중복 키 오류를 처리하고 다시 시도해야합니다.
이를 위해 자신의 솔루션을 굴리지 마십시오. 메시지 큐잉과 마찬가지로 잘못되었을 수 있습니다.
자물쇠를 가진 대량 upsert
때로는 기존의 기존 데이터 세트에 병합 할 새 데이터 세트가있는 대량 업 세트를 수행하려고합니다. 이는 개별 행 업소 트보다 훨씬 효율적이며 가능할 때마다 선호되어야합니다.
이 경우 일반적으로 다음 프로세스를 따릅니다.
CREATE
TEMPORARY
테이블
COPY
또는 임시 테이블에 새 데이터를 대량 삽입
LOCK
목표 테이블 IN EXCLUSIVE MODE
. 이를 통해 다른 트랜잭션 SELECT
은 테이블을 변경할 수 있지만 테이블을 변경할 수는 없습니다.
수행 UPDATE ... FROM
임시 테이블의 값을 사용하여 기존 기록을;
수행 INSERT
이미 대상 테이블에 존재하지 않는 행을;
COMMIT
잠금을 해제합니다.
예를 들어, 질문에 주어진 예제의 경우 다중 값 INSERT
을 사용 하여 임시 테이블을 채우십시오.
BEGIN;
CREATE TEMPORARY TABLE newvals(id integer, somedata text);
INSERT INTO newvals(id, somedata) VALUES (2, 'Joe'), (3, 'Alan');
LOCK TABLE testtable IN EXCLUSIVE MODE;
UPDATE testtable
SET somedata = newvals.somedata
FROM newvals
WHERE newvals.id = testtable.id;
INSERT INTO testtable
SELECT newvals.id, newvals.somedata
FROM newvals
LEFT OUTER JOIN testtable ON (testtable.id = newvals.id)
WHERE testtable.id IS NULL;
COMMIT;
관련 독서
무엇에 대해 MERGE
?
SQL 표준은 MERGE
실제로 동시성 시맨틱이 잘못 정의되어 있으므로 먼저 테이블을 잠그지 않으면 서 업 사이징에 적합하지 않습니다.
데이터 병합에 유용한 OLAP 문이지만 실제로 동시성 안전 upsert에 유용한 솔루션은 아닙니다. 다른 DBMS를 사용하여 upsert에 사용하는 사람들에게 많은 조언이 MERGE
있지만 실제로 잘못되었습니다.
다른 DB :