공공부하자개발 · 영어 학습 노트
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정리‹ 이전다음 ›

4. 응용 변형 예제

예제 5: NULL 함정 시연

이름을 o.name <> n.name 으로만 비교하면 9 번 하나만 나오고, 값이 NULL 로 사라진 4 번은 빠집니다. IS DISTINCT FROM 으로 바꾸면 4 번과 9 번이 나옵니다. 8 번은 양쪽 NULL 이라 두 방식 모두에서 같은 행입니다.

sql
SELECT
       o.id
  FROM old_cust o
  JOIN new_cust n ON n.id = o.id
 WHERE o.name IS DISTINCT FROM n.name
 ORDER BY o.id;
text
ID
--
4
9
(2행)

예제 6: 이관 전 점검: 금액과 날짜

숫자로 바꿀 수 없는 금액은 정규식으로 찾습니다. 정규식 함수는 고급 09 정규식에서 다뤘고, MSSQL 2022 이하에는 없어서 LIKE 나 PATINDEX 를 씁니다. 날짜는 자리수, 월 범위, 일 범위를 순서대로 검사합니다.

sql
SELECT
       id
     , amt
  FROM old_cust
 WHERE NOT REGEXP_LIKE(amt, '^[0-9]+
  

)
 ORDER BY id;
text
ID | AMT
---+----
6  | N/A
(1행)

날짜는 앞 검사를 통과한 값에만 뒤 검사를 해야 하므로 CASE 로 순서를 정합니다. 일 범위는 그 달의 마지막 날과 비교합니다. WHERE 안에서 CAST 를 쓰면 DB 가 계산 순서를 정할 수 있어서 안전하지 않으므로, 안쪽에서 CASE 로 이유를 만들고 바깥에서 거릅니다.

sql
SELECT
       id
     , join_ymd
     , reason
  FROM (
        SELECT
               id
             , join_ymd
             , CASE
                 WHEN NOT REGEXP_LIKE(join_ymd, '^[0-9]{8}
  

) THEN '자리수'
                 WHEN CAST(SUBSTRING(join_ymd, 5, 2) AS INT) NOT BETWEEN 1 AND 12 THEN '월 범위'
                 WHEN CAST(SUBSTRING(join_ymd, 7, 2) AS INT)
                      NOT BETWEEN 1 AND DAY(LAST_DAY(CAST(SUBSTRING(join_ymd, 1, 4) || '-' || SUBSTRING(join_ymd, 5, 2) || '-01' AS DATE))) THEN '일 범위'
               END AS reason
          FROM old_cust
       ) t
 WHERE reason IS NOT NULL
 ORDER BY id;
text
ID | JOIN_YMD | REASON
---+----------+--------
5  | 20240231 | 일 범위
(1행)

4 번의 20240229 는 윤년이라 통과합니다. LAST_DAY 는 Oracle 과 MySQL 함수이고 MSSQL 은 EOMONTH(2012 부터)를 씁니다.

Oracle 의 TRANSLATE(amt, 'x0123456789', 'x') 는 숫자를 지워 남는 글자를 봅니다. H2 의 TRANSLATE 는 글자를 지우지 않아 재현되지 않으므로 문법 검토만이고, Oracle 은 빈 문자열이 NULL 이라 IS NOT NULL 로 판정합니다.

예제 7: 이관 전 점검: 길이 초과와 코드 누락

대상 컬럼이 이름 6 자까지만 받는다고 가정하고 LENGTH 로 비교합니다. 코드 누락은 중급 02 NOT EXISTS 로 코드 표와 맞춥니다.

sql
SELECT
       id
     , name
     , LENGTH(name) AS len
  FROM old_cust
 WHERE LENGTH(name) > 6
 ORDER BY id;
text
ID | NAME             | LEN
---+------------------+----
12 | 조총무팀장대리님 | 8
(1행)
sql
SELECT
       o.id
     , o.grade
  FROM old_cust o
 WHERE NOT EXISTS (SELECT 1 FROM grade_code g WHERE g.code = o.grade)
 ORDER BY o.id;
text
ID | GRADE
---+------
7  | G9
(1행)
주의

Oracle VARCHAR2(n) 은 기본이 BYTE 단위라 한글이 3 바이트로 계산됩니다. 이 H2 결과처럼 문자 수 8 로는 통과해도 실제 Oracle 에서는 바이트 24 라 길이 초과가 나는 것이 이관 중에 흔한 오류입니다. Oracle 에서는 LENGTHB 로 바이트를 확인하거나 컬럼을 VARCHAR2(n CHAR) 로 만듭니다. Oracle 은 구 시스템의 빈 문자열 '' 도 NULL 로 저장해 NOT NULL 컬럼에서 실패하므로 함께 점검합니다.

예제 8: 오류 유형별 건수 요약

행마다 첫 번째 오류 한 가지만 붙여 GROUP BY 로 세면 이관 계획을 세우기 쉽습니다. 한 행에 오류가 둘 이상이면 앞 조건에 걸린 하나만 세어지므로, 총 오류 행수 확인용으로 씁니다.

sql
SELECT
       err
     , COUNT(*) AS cnt
  FROM (
        SELECT
               o.id
             , CASE
                 WHEN NOT REGEXP_LIKE(o.amt, '^[0-9]+
  

) THEN '금액 변환 불가'
                 WHEN NOT REGEXP_LIKE(o.join_ymd, '^[0-9]{8}
  

) THEN '날짜 자리수'
                 WHEN CAST(SUBSTRING(o.join_ymd, 7, 2) AS INT)
                      > DAY(LAST_DAY(CAST(SUBSTRING(o.join_ymd, 1, 4) || '-' || SUBSTRING(o.join_ymd, 5, 2) || '-01' AS DATE))) THEN '날짜 일 범위'
                 WHEN LENGTH(o.name) > 6 THEN '이름 길이 초과'
                 WHEN NOT EXISTS (SELECT 1 FROM grade_code g WHERE g.code = o.grade) THEN '코드 매핑 누락'
                 ELSE '정상'
               END AS err
          FROM old_cust o
       ) t
 GROUP BY err
 ORDER BY cnt DESC, err;
text
ERR            | CNT
---------------+----
정상           | 8
금액 변환 불가 | 1
날짜 일 범위   | 1
이름 길이 초과 | 1
코드 매핑 누락 | 1
(5행)

예제 9: 안전 변환 함수와 DB별 차이 (H2 미지원, 문법 검토만)

아래는 예제 6 의 금액·날짜 점검을 DB의 안전 변환 함수로 쓴 형태입니다. H2 는 이 함수들을 지원하지 않아 실행하지 않았고, 결과는 예제 데이터로 손으로 그린 도식입니다.

sql
-- MSSQL (2012 부터)
SELECT
       id
     , amt
     , join_ymd
  FROM old_cust
 WHERE TRY_CAST(amt AS INT) IS NULL
    OR TRY_CONVERT(DATE, join_ymd, 112) IS NULL;
sql
-- Oracle (12c 릴리스 2 부터)
SELECT
       id
     , amt
     , join_ymd
  FROM old_cust
 WHERE VALIDATE_CONVERSION(amt AS NUMBER) = 0
    OR VALIDATE_CONVERSION(join_ymd AS DATE, 'YYYYMMDD') = 0;
sql
-- MySQL
SELECT
       id
     , amt
     , join_ymd
  FROM old_cust
 WHERE amt NOT REGEXP '^[0-9]+
  


    OR STR_TO_DATE(join_ymd, '%Y%m%d') IS NULL;

도식(H2 미지원, 실행 결과 아님)

text
id | amt | join_ymd
---+-----+---------
5  | 2000| 20240231
6  | N/A | 20240412

값을 바꾸며 실패를 NULL 로 받으려면 Oracle 은 CAST(amt AS NUMBER DEFAULT NULL ON CONVERSION ERROR) 를 씁니다. MySQL 의 STR_TO_DATE 는 sql_mode 가 엄격하면 NULL 대신 오류가 되므로 세션 설정을 확인합니다.

예제 10: DB별 차집합과 NULL 비교, 해시 대사

방언 전환 모드로 차집합 문법을 실행합니다. Oracle 모드의 MINUS 는 예제 3 과 같은 4 건을 냅니다. Oracle 의 NVL 과 MSSQL 의 ISNULL 로 NULL 을 자리 표시 값으로 바꿔 비교하는 방법도 소스 2, 4번에서 실행하며 4 번과 9 번이 잡힙니다.

sql
SET MODE Oracle;
SELECT id FROM old_cust
MINUS
SELECT id FROM new_cust
 ORDER BY id;
text
ID
--
5
6
7
12
(4행)

MySQL 모드와 MSSQL 모드에서도 EXCEPT 가 같은 결과를 냅니다(소스 3, 4번). 실제 MySQL 은 EXCEPT 를 8.0.31 부터, Oracle 은 21c 부터 지원하고 그 전에는 MINUS 입니다.

해시 대사는 행 전체를 구분자로 이어 붙여 안쪽 뷰에서 해시를 만들고 키로 맞물려 비교합니다(소스 5번, H2 의 HASH 함수). 이 데이터에서는 예제 4 와 같은 3, 4, 9, 10, 11 번 다섯 행이 다르다고 나옵니다. 그 예는 NULL 을 빈 문자열로 바꿔서 이름 NULL 과 이름 '' 가 같은 해시가 되는 약점이 있으므로, 구분이 필요하면 NULL 을 다른 표시 값으로 바꿉니다.

응용 변형 예제
  • 예제 5: NULL 함정 시연
  • 예제 6: 이관 전 점검: 금액과 날짜
  • 예제 7: 이관 전 점검: 길이 초과와 코드 누락
  • 예제 8: 오류 유형별 건수 요약
  • 예제 9: 안전 변환 함수와 DB별 차이 (H2 미지원, 문법 검토만)
  • 예제 10: DB별 차집합과 NULL 비교, 해시 대사
이전 섹션3 코드 예제4 / 6다음 섹션5 자주 하는 실수 (Tip)