MERGE INTO(UPSERT) 에서도 PK violation이 발생할 수 있다.
상황
- 요구사항 1) tbl에 없는 경우 INSERT. 있다면 에러 없이 스킵 처리
- 요구사항 2) 동시에 여러 스레드에서 쿼리 실행해야 함
```sql
MERGE INTO tbl
USING dual
ON (pk1 = '1')
WHEN NOT MATCHED THEN
INSERT (pk1) VALUES ('1')
```
```
; SQL []; ORA-00001: 무결성 제약 조건(TBL.IPK_PK1)에 위배됩니다
; nested exception is java.sql.SQLIntegrityConstraintViolationException: ORA-00001: 무결성 제약 조건(TBL.IPK_PK1)에 위배됩니다
```
- tbl에 없는 경우 INSERT임에도, PK violation이 발생했음.
- (만약 constraint가 없었다면 같은 pk1=1 이 중복해서 저장되었을 것임)
- ThreadPool count 1로 주고 실행하면 dup key 에러 없이 실행됨.
원인 : race condition
- https://stackoverflow.com/questions/9871644/is-merge-an-atomic-statement-in-sql2008
- https://hrjeong.tistory.com/377
- MERGE 구문은 ON 절에서 read lock, INSERT/UPDATE 에서 update lock을 건다
- 즉, MERGE 구문은 all or nothing의 의미로 atomic하긴 하지만, 독점적 lock을 획득하고 중간에 끼어드는 것 없이 작업을 완수한다는 의미에서는 atomic하지 않다.
- 더 상세한 내용 https://weblogs.sqlteam.com/dang/2009/01/31/upsert-race-condition-with-merge/
이러한 속성 때문에 MERGE INTO를 tbl에 없는 경우 INSERT, 있다면 에러 없이 스킵 처리에 사용하는 것은 적절하지 않을 수 있다.
해결방안
- 앱단에서 ORA-00001 catch 해서 dupkey 무시
- BEGIN... EXCEPTION... END; 구문 사용해서 쿼리 단에서 dupkey 무시
- ignore_row_on_dupkey_index 힌트 사용해서 dupkey 무시
- 이 힌트는 특정한 경우 오라클 11g <> 19c 동작이 다르기 때문에 주의
비슷한 이슈
'RDBMS > Query design' 카테고리의 다른 글
인덱스 관련 총정리 (0) | 2023.08.15 |
---|---|
인덱스 힌트 관련 - 쿼리 플랜 결과는 무시하고 꼭 써줘야 한다. (0) | 2023.04.25 |
[Oracle] Pagination (0) | 2021.05.17 |
[Oracle] longest match (0) | 2021.02.16 |
일단 다 가져와서 앱에서 필터링? vs 쿼리 WHERE에서 필터링? (0) | 2019.11.28 |