공공부하자개발 · 영어 학습 노트
SQL
SQL 실무페이징·검색·이력·통계·채번·이관0/9 완료
  • 01페이징 쿼리
  • 02동적 검색 조건
  • 03이력 테이블과 시점 조회
  • 04기간별 통계 보고서
  • 05중복 데이터 찾기와 정리
  • 06트랜잭션 기초
  • 07채번과 순번 관리
  • 08저장 프로시저·함수 기초
  • 09데이터 이관·검증 쿼리
사이트 소개개인정보처리방침연락처
© 2026 공부하자
홈 › SQL 실무 › 09 / 9

데이터 이관·검증 쿼리

건수·합계 대사, 차이 행 찾기, 변환 점검
섹션 6진행 0 / 9
1왜 배우는가2핵심 원리3코드 예제4응용 변형 예제5자주 하는 실수 (Tip)6정리‹ 이전다음 ›

3. 코드 예제

소스: sql-src/work_09_migration_check/01_reconcile.sql, 02_convert_check.sql.

구 시스템 스테이징 표 old_cust(id, name, join_ymd 문자 8자리, amt 문자, grade)에 12행, 새 표 new_cust(id, name, join_dt DATE, amt INT, grade 는 코드 표 FK)에 9행이 있습니다. 코드 표 grade_code 에는 G1, G2, G3 세 개가 있습니다.

구 시스템에는 일부러 문제를 넣었습니다. 5 번은 '20240231' 이라는 존재하지 않는 날짜, 6 번은 금액 'N/A', 7 번은 코드 표에 없는 G9, 12 번은 이름이 너무 깁니다. 이 네 행은 이관되지 않았습니다. 신 표에는 구에 없는 99 번이 있고 3, 4, 9, 10, 11 번은 값이 다릅니다.

예제 1: 건수·합계·최솟값·최댓값을 한 결과로

양쪽 집계를 UNION ALL 로 두 행에 붙이면 한눈에 비교됩니다. 구의 금액은 문자이므로 숫자 모양인 행만 CAST 해서 더합니다. 날짜는 신 쪽을 8자리 문자로 바꿔 구와 같은 형태로 맞춥니다.

sql
SELECT
       'old' AS src
     , COUNT(*) AS cnt
     , SUM(CASE WHEN REGEXP_LIKE(amt, '^[0-9]+
  

) THEN CAST(amt AS INT) END) AS sum_amt
     , MIN(join_ymd) AS min_ymd
     , MAX(join_ymd) AS max_ymd
  FROM old_cust
 UNION ALL
SELECT
       'new'
     , COUNT(*)
     , SUM(amt)
     , MIN(TO_CHAR(join_dt, 'YYYYMMDD'))
     , MAX(TO_CHAR(join_dt, 'YYYYMMDD'))
  FROM new_cust;
text
SRC | CNT | SUM_AMT | MIN_YMD  | MAX_YMD
----+-----+---------+----------+---------
old | 12  | 28000   | 20240105 | 20241003
new | 9   | 20700   | 20240105 | 20241010
(2행)

건수가 3 건 줄었고, 합계도 다릅니다. 최댓값이 신에서 더 큰 것은 구에 없는 99 번(20241010) 때문입니다. 건수 차이는 누락 4 건과 초과 1 건이 겹친 결과이므로, 차이 3 만 보고 누락 3 건이라고 결론 내리면 안 됩니다.

예제 2: 코드별 건수 대사

전체 건수가 맞아도 코드별로는 어긋날 수 있습니다. 코드 표를 기준으로 양쪽 집계를 LEFT JOIN 하면 한쪽에 아예 없는 코드도 0 건으로 나옵니다. 신에 없는 코드는 구 쪽 집계에만 있을 수 있으므로 코드 표를 기준 축으로 씁니다.

sql
SELECT
       g.code
     , COALESCE(o.cnt, 0) AS old_cnt
     , COALESCE(n.cnt, 0) AS new_cnt
     , COALESCE(n.cnt, 0) - COALESCE(o.cnt, 0) AS diff
  FROM grade_code g
  LEFT JOIN (SELECT grade, COUNT(*) AS cnt FROM old_cust GROUP BY grade) o ON o.grade = g.code
  LEFT JOIN (SELECT grade, COUNT(*) AS cnt FROM new_cust GROUP BY grade) n ON n.grade = g.code
 ORDER BY g.code;
text
CODE | OLD_CNT | NEW_CNT | DIFF
-----+---------+---------+-----
G1   | 4       | 5       | 1
G2   | 4       | 2       | -2
G3   | 3       | 2       | -1
(3행)

G1 만 신이 많은 것은 99 번과 등급이 바뀐 11 번 때문입니다.

예제 3: 양방향 EXCEPT 로 누락과 초과 찾기

구에서 신을 빼면 누락이고, 신에서 구를 빼면 초과입니다. 키 하나만 비교하면 가장 빠릅니다.

sql
SELECT id FROM old_cust
EXCEPT
SELECT id FROM new_cust
 ORDER BY id;
SELECT id FROM new_cust
EXCEPT
SELECT id FROM old_cust
 ORDER BY id;
text
ID
--
5
6
7
12
(4행)
text
ID
--
99
(1행)

누락 4 건은 원천 오류 행과 같고, 초과 99 번은 신에 직접 넣었는지 확인합니다. 한 방향만 봤다면 놓쳤을 행입니다.

예제 4: 값이 다른 행과 다른 컬럼 표시

키로 조인해서 컬럼별로 같은지 0 과 1 로 표시합니다. 신 값을 구와 같은 문자 형태로 되돌려 비교하면 구 쪽 변환 오류를 피할 수 있습니다. 플래그를 안쪽 뷰에서 만들고 합이 0 보다 큰 행만 바깥에서 거릅니다. 이렇게 하면 WHERE 에 같은 비교를 되풀이하지 않아도 됩니다.

sql
SELECT
       id
     , name_diff
     , ymd_diff
     , amt_diff
     , grade_diff
  FROM (
        SELECT
               o.id
             , CASE WHEN o.name = n.name OR (o.name IS NULL AND n.name IS NULL) THEN 0 ELSE 1 END AS name_diff
             , CASE WHEN o.join_ymd = TO_CHAR(n.join_dt, 'YYYYMMDD') THEN 0 ELSE 1 END AS ymd_diff
             , CASE WHEN o.amt = CAST(n.amt AS VARCHAR) THEN 0 ELSE 1 END AS amt_diff
             , CASE WHEN o.grade = n.grade THEN 0 ELSE 1 END AS grade_diff
          FROM old_cust o
          JOIN new_cust n ON n.id = o.id
       ) t
 WHERE name_diff + ymd_diff + amt_diff + grade_diff > 0
 ORDER BY id;
text
ID | NAME_DIFF | YMD_DIFF | AMT_DIFF | GRADE_DIFF
---+-----------+----------+----------+-----------
3  | 0         | 0        | 1        | 0
4  | 1         | 0        | 0        | 0
9  | 1         | 0        | 0        | 0
10 | 0         | 1        | 0        | 0
11 | 0         | 0        | 0        | 1
(5행)

3 번은 금액, 4 번은 이름이 NULL 로 사라졌고, 9 번은 이름이 달라졌고, 10 번은 가입일이 하루 밀렸고, 11 번은 등급이 바뀌었습니다. 8 번은 구와 신이 모두 이름 NULL 이라 같은 행으로 봤으므로 나오지 않습니다. CASE 의 첫 조건이 NULL 을 알 수 없음으로 만들면 ELSE 로 떨어지는 점이 이 식이 4 번을 잡는 이유입니다.

예제 직접 실행

아래 폴더의 SQL 파일을 Git Bash 에서 H2 메모리 DB 로 실행합니다. 방언은 파일 안의 SET MODE 로 바꿉니다.

cd sql-src/work_09_migration_check
ls *.sql
bash ../run.sh <파일>.sql
코드 예제
  • 예제 1: 건수·합계·최솟값·최댓값을 한 결과로
  • 예제 2: 코드별 건수 대사
  • 예제 3: 양방향 EXCEPT 로 누락과 초과 찾기
  • 예제 4: 값이 다른 행과 다른 컬럼 표시
이전 섹션2 핵심 원리3 / 6다음 섹션4 응용 변형 예제