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

11월 판매상품 4만 5천 행을 다시 넣는 데 SQL이 9만 번 실행되고, 지운 상품 번호가 판매상품에 남아 있다

중급INSERT유통472 / 700

상황 ​

본사 레거시 유통의 판매상품 CF11에는 2016년에 만든 검증 트리거가 있다. 판매 번호와 상품 번호가 실제로 있는지, 수량이 양수인지 행마다 확인한다. 이번 주 매장 POS 서버를 바꾸면서 11월 판매상품 45,370행을 다시 받아 넣었는데(기존 11월분은 지웠다), 이 INSERT 한 문장에서 SQL이 9만 번 넘게 실행됐다. 같은 주 감사 점검에서는 상품 마스터에 없는 상품 번호를 가진 판매상품 2행이 나왔다. 개발팀: "트리거가 다 막는데 어떻게 들어갔는지 모르겠어요."

테이블과 인덱스 ​

  • CF10 판매: 500,000행 · 4,583블록 · CF10_PK (SLE_NO) · 11월 20,712건
  • CF11 판매상품: 1,096,708행 · 8,204블록 · CF11_PK (SLE_NO, ITM_NO) · 이 문제에서는 FK가 없고 트리거 TRG_CF11_CHK가 대신한다. ITM_NO가 맨 앞인 인덱스는 없다.
  • CF01 상품: 109행 · 5블록 · PK ITM_NO · 분기마다 「미출시 상품 정리」 배치가 판매 이력이 없는 상품을 지운다.
  • CF11_RCV 수신 테이블: 11월 판매상품 45,370행. 반품은 다른 테이블이라 SLE_QTY는 언제나 양수여야 한다.
  • POS는 계산 한 건을 트랜잭션 하나로 묶어 넣는다. 매장 6곳, 계산대 14대.

원본 SQL ​

sql
-- 2016년부터 있는 검증 트리거
CREATE OR REPLACE TRIGGER TRG_CF11_CHK
BEFORE INSERT OR UPDATE ON CF11
FOR EACH ROW
DECLARE
  v_cnt NUMBER;
BEGIN
  SELECT COUNT(*) INTO v_cnt FROM CF10 WHERE SLE_NO = :NEW.SLE_NO;
  IF v_cnt = 0 THEN RAISE_APPLICATION_ERROR(-20101, '판매 없음 ' || :NEW.SLE_NO); END IF;

  SELECT COUNT(*) INTO v_cnt FROM CF01 WHERE ITM_NO = :NEW.ITM_NO;
  IF v_cnt = 0 THEN RAISE_APPLICATION_ERROR(-20102, '상품 없음 ' || :NEW.ITM_NO); END IF;

  IF :NEW.SLE_QTY <= 0 THEN RAISE_APPLICATION_ERROR(-20103, '수량 오류'); END IF;
END;

-- 이번 주 · 11월분 다시 넣기
INSERT INTO CF11 (SLE_NO, ITM_NO, SLE_QTY, SLE_UPRC)
SELECT SLE_NO, ITM_NO, SLE_QTY, SLE_UPRC
  FROM CF11_RCV;
COMMIT;

실행계획 학습용 예시 ​

xplan
[11월분 다시 넣기 — 세션 통계]
  경과 시간                  3.84 초
  CPU used by this session   3.41 초
  recursive calls            91,032
  execute count              90,756
  session logical reads      238,640
  redo size                  14.6 MB

[TKPROF — 트리거 안의 두 문장]
SELECT COUNT(*) FROM CF10 WHERE SLE_NO = :B1
call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Execute  45370      0.61       0.66          0          0          0           0
Fetch    45370      0.38       0.41          0     136110          0       45370

SELECT COUNT(*) FROM CF01 WHERE ITM_NO = :B1
call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Execute  45370      0.55       0.60          0          0          0           0
Fetch    45370      0.29       0.31          0      45370          0       45370

[감사 점검 — 고아 행 2행의 내력]
  CF11에 있는데 CF01에 없는 ITM_NO      1088 · 2행
  두 행을 넣은 POS 트랜잭션             3월 14일 10:02:41 INSERT (트리거 통과) · 10:03:20 COMMIT
  상품 1088을 지운 정리 배치             3월 14일 10:02:30 DELETE · 10:02:44 COMMIT

문제 ​

  1. INSERT 한 문장에서 SQL이 9만 번 넘게 실행된 이유를 TKPROF로 설명하시오.
  2. 트리거가 있는데도 고아 행 2행이 들어간 이유를 두 트랜잭션의 시각으로 설명하시오.
  3. 검사를 제약으로 옮기도록 고치고, 그 제약을 켜기 전에 할 일과 켠 뒤 생기는 대가를 쓰시오.
원본 SQL을 고쳐 쓰세요. 인덱스 DDL과 힌트도 쓸 수 있습니다.
무엇이 문제였고 왜 이렇게 고쳤는지 쓰세요.0자