백엔드DB / SQL
5

JOIN 키가 없었다 — Oracle에서 col_co를 복구한 과정

ROW_NUMBER()와 MERGE INTO로 Oracle에서 안전하게 코드를 복구한 과정을 정리했다.

JOIN 키가 없었다 — Oracle에서 col_co를 복구한 과정

일단 테이블 구조부터

이번 작업에 등장하는 테이블은 두 개다.

테이블A (마스터 테이블)

특정 엔티티의 코드와 이름을 관리하는 마스터 테이블. col_id(식별코드) + col_co(회사코드)가 복합 PK다.

컬럼명타입설명
col_idVARCHAR2 NOT NULL🔑 식별코드 (PK)
col_coVARCHAR2 NOT NULL🔑 회사코드 (PK)
col_nmVARCHAR2 NOT NULL이름 ← 이게 매핑 기준
col_bizVARCHAR2기타 정보
col_ynVARCHAR2(1) NOT NULL상태여부
col_regDATE NOT NULL등록일시
col_updDATE NOT NULL수정일시

여기서 col_nm으로 항목을 찾아서 col_id를 가져오는 게 핵심이다.


테이블B (트랜잭션 테이블)

실제 업무 데이터를 관리하는 테이블. col_co + col_no가 복합 PK.
문제는 여기에 col_nm 컬럼이 없다는 것. col_id만 있는데, 그 값이 잘못 들어가 있는 상태다.

컬럼명타입설명
col_coVARCHAR2 NOT NULL🔑 회사코드 (PK)
col_noVARCHAR2 NOT NULL🔑 일련번호 (PK)
col_idVARCHAR2⚠️ 식별코드 — 오늘 고쳐야 할 컬럼
col_nm2VARCHAR2 NOT NULL항목명
col_dt1VARCHAR2(8)시작일자
col_dt2VARCHAR2(8)종료일자
col_regDATE NOT NULL등록일시
col_updDATE NOT NULL수정일시

둘의 관계를 그림으로 보면

테이블A (마스터)
┌──────────────────────────────────────┐
│ col_id  VARCHAR2  PK  ◄── 정답 코드  │──┐
│ col_co  VARCHAR2  PK                 │  │ col_nm으로 찾아서
│ col_nm  VARCHAR2 ← 매핑 기준         │  │ col_id를 가져옴
└──────────────────────────────────────┘  │
                                          │
테이블B (트랜잭션)                           │
┌──────────────────────────────────────┐  │
│ col_co  VARCHAR2  PK                 │  │
│ col_no  VARCHAR2  PK                 │  │
│ col_id  VARCHAR2 ──────────────────  ┼──┘ ← 잘못된 값 → 복구 대상
│ col_nm2 VARCHAR2                     │
│ (col_nm 없음!)                       │
└──────────────────────────────────────┘

처음엔 쉬울 줄 알았다

외부에서 이름 목록 N건을 받았고, 테이블A에서 코드를 찾아 테이블B에 넣으면 끝이라고 생각했다.

-- 처음에 떠올린 방법
UPDATE 테이블B H
SET H.col_id = (
    SELECT A.col_id
    FROM 테이블A A
    WHERE A.col_nm = H.col_nm  -- ← 근데 이 컬럼이 없잖아...
)

바로 막혔다. 테이블B에 col_nm이 없으니까 JOIN 자체가 불가능한 상황.
그래서 외부에서 받은 이름 목록을 다른 방식으로 활용해야 했다.


두 번째 함정: 같은 이름인데 코드가 다르다?

테이블A를 들여다봤더니 이런 게 있었다.

col_idcol_nm
C001AAA(주)
C002AAA
C003AAA㈜

응? 셋 다 같은 항목 아닌가? 맞다. 근데 코드가 다르다.
(주), , 아무것도 없는 것 — 표기 방식에 따라 별개 코드로 등록돼 있었다.

표기코드
BBB㈜C010
BBBC011
㈜CCCC020
CCCC021

이 상태에서 LIKETRIM으로 퉁치면 어떻게 될까? 엉뚱한 코드가 들어간다.
표기 그대로 정확히 매핑하는 수밖에 없었다.


Python으로 깔끔하게 해결

N개를 손으로 매핑하다가 실수하면 답이 없다. Python 딕셔너리로 자동화했다.

# 테이블A 기준으로 만든 매핑 딕셔너리 (이름 → 코드)
mapping = {
    "AAA":     "C001",
    "AAA(주)": "C002",
    "AAA㈜":   "C003",
    "㈜BBB":   "C010",
    "BBB":     "C011",
    # ... 총 N개
}

# 외부에서 받은 이름 목록
nm_list = ["AAA", "㈜BBB", ...]

# 매핑 실행
results = []
for nm in nm_list:
    code = mapping.get(nm.strip(), "NOT_FOUND")
    results.append(code)

print(results)  # 미매칭 0건!

결과: N개 전부 매칭 성공. NOT_FOUND 하나도 없었다.


본론: MERGE INTO + ROW_NUMBER()

왜 ROW_NUMBER()를 썼냐면

테이블B에 col_nm이 없으니 JOIN 키가 없다.
그래서 Python이 뽑아준 코드 순서col_no 오름차순 순서를 맞춰서 매핑하는 방식을 썼다.

여기서 순서 보장이 핵심인데, ROWNUM을 쓰면 낭패 본다.

ROWNUM이 왜 위험한가?

구분ROWNUMROW_NUMBER()
정렬 보장❌ 안 됨✅ ORDER BY로 확정
사용 위치WHERE 절SELECT 절 (윈도우 함수)
안전성낮음높음

ORDER BY를 붙여도 ROWNUM은 정렬 에 번호가 붙는다.
실행할 때마다 순서가 달라질 수 있다는 뜻. 무조건 ROW_NUMBER() OVER (ORDER BY col_no)로 가야 한다.

최종 SQL

MERGE INTO 테이블B A
USING (
  SELECT col_no, ROW_NUMBER() OVER (ORDER BY col_no) AS RN
  FROM 테이블B
) B ON (A.col_no = B.col_no)
WHEN MATCHED THEN UPDATE SET A.col_id =
  CASE B.RN
    WHEN 1 THEN 'C001'
    WHEN 2 THEN 'C002'
    WHEN 3 THEN 'C003'
    -- Python이 생성한 N개 매핑
    WHEN N THEN 'C0NN'
  END;

단순 UPDATE 대신 MERGE INTO를 쓴 이유는 매칭 실패 시 에러 없이 넘어가고,
필요하면 WHEN NOT MATCHED → INSERT로 확장도 되기 때문이다.

-- MERGE INTO 기본 구조
MERGE INTO [대상 테이블] A
USING [기준 서브쿼리 or 테이블] B
ON (A.키 = B.키)
WHEN MATCHED THEN UPDATE SET ...
WHEN NOT MATCHED THEN INSERT ...  -- 선택사항

실행할 때는 꼭 이 순서대로

-- 1. 먼저 현재 데이터 확인 (행 수가 N건인지 체크!)
SELECT col_no, col_id FROM 테이블B ORDER BY col_no;

-- 2. MERGE 실행 (아직 커밋 아님)
MERGE INTO 테이블B A
USING ( ... ) B ON (A.col_no = B.col_no)
WHEN MATCHED THEN UPDATE SET A.col_id = CASE B.RN ... END;

-- 3. 결과 다시 확인
SELECT col_no, col_id FROM 테이블B ORDER BY col_no;

-- 4-A. 맞으면 커밋
COMMIT;

-- 4-B. 이상하면 롤백
ROLLBACK;

⚠️ Oracle은 DML 후 자동 커밋이 안 된다. 눈으로 확인하고 커밋하자.
1번에서 행 수가 정확히 N건이어야 순서 매핑이 맞게 들어간다.


마무리 정리

  • 테이블B에 col_nm이 없어서 JOIN UPDATE는 처음부터 불가능했다.
  • 같은 항목이라도 표기 차이로 코드가 달라지는 케이스는 Python 딕셔너리로만 안전하게 잡을 수 있었다.
  • Oracle 순서 기반 UPDATE에서 ROWNUM은 쓰면 안 된다. ROW_NUMBER() OVER (ORDER BY 컬럼) 이 답이다.
  • UPDATE보다 MERGE INTO가 더 유연하고 안전하다.
#SQL#Oracle

댓글

(0)
JOIN 키가 없었다 — Oracle에서 col_co를 복구한 과정 | 강민석의 개발블로그