터미널에서 동일한 데이터를 두 번 가져오는 작업을 실행했습니다. 첫 번째는 1건이 등록되었고, 두 번째는 0건이었습니다. 여기까지는 순조로웠습니다. 그런데 동일한 주문 ID의 금액을 변경했음에도 불구하고 오류 없이 0건으로 처리되었습니다.
저는 단순히 '중복되면 무시한다'는 방식의 처리를 업무 데이터 가져오기에는 사용하고 싶지 않습니다. 재실행 시 데이터를 늘리지 않으면서도, 수정된 데이터가 덮어쓰여지는 것을 방지하는 것이 중요합니다.
가상의 주문 데이터를 사용하여 재현했습니다. CSV를 읽은 후 등록 부분에 사용할 예시입니다. ID는 문자열(string), 금액은 정수(integer)로 전달합니다.
ON CONFLICT(id) DO NOTHING
이 구문은 동일한 ID가 존재하면 삽입을 건너뜁니다. 기존 행의 금액과 일치하는지까지는 확인하지 않습니다.
첫 번째 가져오기 시 1,000원이었고, 재가져오기 시 1,100원이더라도 ID가 같으면 그냥 넘어갑니다. 만약 건수만 로그로 남긴다면, 동일한 파일을 처리했는지 아니면 수정 데이터를 버렸는지 구분할 수 없습니다.
그래서 삽입을 건너뛴 행에 대해서만 기존 금액과 대조합니다. 일치하면 재가져오기로 간주하여 무시하고, 다르면 예외를 발생시켜 해당 회차의 가져오기 작업을 전부 취소하는 방침을 세웠습니다.
파이썬 3.12 이상이 필요합니다. CPython 3.14.8과 SQLite 3.54.1로 실행했습니다. import_check.py에 저장하고, python3 import_check.py로 실행할 수 있습니다.
import sqlite3
db = sqlite3.connect(":memory:", autocommit=False)
db.execute("""
...
출력은 다음과 같았습니다.
첫 번째: 1
재가져오기: 0
A001: 금액이 기존 행과 다릅니다
...
세 번째 실행에서는 A002를 삽입한 후, A001의 금액 차이를 발견했습니다. 마지막 목록에 A002가 남아있지 않은 곳까지 확인합니다.
제가 처음에 본 것은 오류가 발생하는지 여부뿐이었습니다. 이전에 들어간 행까지 확인하지 않으면, 절반만 등록된 가져오기를 놓칠 수 있습니다.
autocommit=False로 트랜잭션(transaction)을 사용하고, with db: 블록을 정상적으로 빠져나가면 확정(commit)되고, 예외(exception)가 발생하면 취소(rollback)됩니다. 실행 환경의 원본 자료도 확인했습니다. help(sqlite3.Connection.__exit__)에 이 확정과 취소 동작이 설명되어 있습니다.
except 블록은 with db: 외부여야 합니다. 내부에서 예외를 잡아먹고 정상 종료시키면, 중간까지 삽입된 행이 확정될 수 있습니다. 오류를 표시할 위치는 단순히 선호의 문제가 아닙니다.
빈 ID도 별도로 걸러내고 싶습니다. NOT NULL로 거부되는 것은 None에 해당하는 NULL 값이며, 빈 문자열(empty string)은 통과합니다. 가져오기 원본에서 공백만 들어있는 ID를 허용할지는 사전에 결정해야 합니다.
이 예시는 금액만 대조합니다. 납품처나 품목도 다룰 거라면, 그것들도 비교 대상에 추가해야 합니다. 명세(detail)가 여러 개인 주문의 경우, 주문 ID와 명세 번호 조합을 키로 사용합니다. 회사 이름을 키로 하면 다른 주문까지 중복 처리됩니다.
실제 데이터는 연결 대상을 :memory:에서 파일 이름으로 변경하고, 재실행 시에도 동일한 DB를 엽니다. 기존 행의 수정 절차는 별도로 필요합니다.
자동 가져오기를 맡기는 범위가 넓어질수록, 실패 후에 같은 데이터를 다시 흘려보낼 기회도 늘어난다고 생각합니다. 성공 건수만으로는 그 재실행을 평가할 수 없습니다. 신규 등록, 동일 내용의 재전송, 내용이 변경된 재전송 세 가지 패턴에 대해 남아있는 행들을 각각 확인해야 합니다. 본인의 가져오기 처리에도 이 3가지 패턴을 적용하여, 오류 발생 후 저장된 행까지 반드시 확인하십시오.