Skip to content
튜닝 연습DML과 배치 튜닝
DML-026

POS 재전송분 적재가 가끔 ORA-00001로 멈추고, 멈추지 않는 날에도 새 주문을 두 번 쓴다

중급MERGE버거451 / 700

상황 ​

버거원 본사는 매일 04:00에 매장 POS가 보낸 주문 파일을 ORD_STG에 받아 ORD에 반영한다. 파일에는 새 주문과, 매장이 정정해 다시 보낸 주문(재전송)이 섞여 있다. 지금 배치는 새 주문을 INSERT한 다음, 파일에 있는 주문을 모두 UPDATE한다. 한 달에 두세 번은 POS 재시도로 같은 주문이 한 파일에 두 번 들어와 INSERT가 ORA-00001로 멈춘다. 그러면 아침에 운영 담당이 중복 행을 손으로 지우고 다시 돌린다.

테이블과 인덱스 ​

  • ORD 주문: 1,000,000행 · 22,422블록 · ORD_PK (ORD_NO) 높이 3 · ORD_X01 (ORD_DT, ORD_HMS)
  • ORD_STG(적재용, 날마다 비우고 받는다): 34블록 · ORD_NO, ORD_DT, ORD_HMS, SHOP_NO, RCV_SEQ(수신 순번) · 제약과 인덱스는 없다
  • 오늘 파일 1,452행: 새 주문 1,371건 + 재전송 79건(그중 값이 실제로 바뀐 것 64건) + 재시도 중복 2행(새 주문 2건이 두 번씩 들어왔다)
  • 같은 주문이 두 번 오면 나중에 받은 것(RCV_SEQ가 큰 쪽)이 맞다. 세 칸(ORD_DT, ORD_HMS, SHOP_NO)은 NOT NULL이다.

원본 SQL ​

sql
-- ① 새 주문
INSERT INTO ORD (ORD_NO, ORD_DT, ORD_HMS, SHOP_NO)
SELECT s.ORD_NO, s.ORD_DT, s.ORD_HMS, s.SHOP_NO
  FROM ORD_STG s
 WHERE NOT EXISTS (SELECT 1 FROM ORD o WHERE o.ORD_NO = s.ORD_NO);

-- ② 정정 (파일에 있는 주문은 모두)
UPDATE ORD o
   SET (o.ORD_DT, o.ORD_HMS, o.SHOP_NO) =
       (SELECT s.ORD_DT, s.ORD_HMS, s.SHOP_NO FROM ORD_STG s WHERE s.ORD_NO = o.ORD_NO)
 WHERE o.ORD_NO IN (SELECT s.ORD_NO FROM ORD_STG s);
COMMIT;

실행계획 학습용 예시 ​

xplan
[중복이 없던 어제 · 1,450행 파일 — ① INSERT 위 · ② UPDATE 아래]
---------------------------------------------------------------------------------------------
| Id  | Operation                | Name    | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
---------------------------------------------------------------------------------------------
|   0 | INSERT STATEMENT         |         |      1 |        |      0 |00:00:00.08 |   10566 |
|   1 |  LOAD TABLE CONVENTIONAL | ORD     |      1 |        |      0 |00:00:00.08 |   10566 |
|   2 |   NESTED LOOPS ANTI      |         |      1 |   1450 |   1371 |00:00:00.01 |    4384 |
|   3 |    TABLE ACCESS FULL     | ORD_STG |      1 |   1450 |   1450 |00:00:00.01 |      34 |
|*  4 |    INDEX UNIQUE SCAN     | ORD_PK  |   1450 |      1 |     79 |00:00:00.01 |    4350 |
---------------------------------------------------------------------------------------------
|   0 | UPDATE STATEMENT         |         |      1 |        |      0 |00:00:00.24 |   58075 |
|   1 |  UPDATE                  | ORD     |      1 |        |      0 |00:00:00.24 |   58075 |
|   2 |   NESTED LOOPS           |         |      1 |   1450 |   1450 |00:00:00.01 |    4384 |
|   3 |    SORT UNIQUE           |         |      1 |   1450 |   1450 |00:00:00.01 |      34 |
|   4 |     TABLE ACCESS FULL    | ORD_STG |      1 |   1450 |   1450 |00:00:00.01 |      34 |
|*  5 |    INDEX UNIQUE SCAN     | ORD_PK  |   1450 |      1 |   1450 |00:00:00.01 |    4350 |
|*  6 |   TABLE ACCESS FULL      | ORD_STG |   1450 |      1 |   1450 |00:00:00.05 |   49300 |
---------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
   ① 4 - access("O"."ORD_NO"="S"."ORD_NO")
   ② 5 - access("O"."ORD_NO"="S"."ORD_NO")
      6 - filter("S"."ORD_NO"=:B1)

[중복 2행이 있던 오늘 — ① INSERT]
  ORA-00001: unique constraint (DEV.ORD_PK) violated   → 문장 전체가 되돌려지고 배치가 멈췄다

문제 ​

  1. 오늘 INSERT가 ORA-00001로 멈춘 이유를 설명하시오. 중복이 정정 주문에 생겼다면 UPDATE는 어떻게 되는지도 쓰시오.
  2. 멈추지 않은 어제의 두 문장이 한 일을 실행계획으로 설명하시오. 특히 UPDATE의 A-Rows 1,450이 무엇인지 설명하시오.
  3. 한 문장으로 다시 작성하시오. 재시도 중복은 나중에 받은 것을 쓰고, 값이 바뀐 정정만 고치게 하시오.
원본 SQL을 고쳐 쓰세요. 인덱스 DDL과 힌트도 쓸 수 있습니다.
무엇이 문제였고 왜 이렇게 고쳤는지 쓰세요.0자