두 표를 합쳤더니 행이 늘었다: JOIN 키의 중복과 관계 확인

이 카테고리 글 목록

전체글 보기

두 표를 합친 뒤 행이 늘었다고 해서 곧바로 중복 오류인 것은 아니다. 같은 키가 양쪽에 몇 번 나타나는지, 결과 한 행이 무엇을 뜻해야 하는지부터 확인해야 한다. 키 A가 왼쪽에 두 건, 오른쪽에 세 건 있으면 해당 키의 모든 조합이 여섯 행으로 나올 수 있다. 필요한 관계라면 정상이고, 한 대상에 한 행을 기대했다면 결합 조건이나 입력의 단위를 다시 봐야 한다.

이 글은 문서 AI·CSV에서 확보한 표를 고객·주문·처리 이력과 결합하는 담당자를 위한 설명이다. 아래 ID와 건수는 합성 자료다. Python 3.12.14와 pandas 3.0.1에서 작은 예제를 확인했으며 실제 고객 데이터·운영 DB나 대용량 성능을 시험하지 않았다.

같은 키의 건수를 곱하면 늘어난 행의 위치가 보인다

왼쪽 표의 키를 A, A, B, L, 오른쪽 표의 키를 A, A, A, B, R로 만들었다. 각 행에는 서로 다른 left_id 또는 right_id가 있어, 같은 키라도 별개의 입력 행이다. pandas의 결합 안내는 같은 키 조합이 양쪽에 여러 번 있으면 해당 자료의 모든 조합이 생긴다고 설명한다.

키 왼쪽 건수 오른쪽 건수 이 예의 inner 결과
A 2 3 6행
B 1 1 1행
L 1 0 0행
R 0 1 0행

실제 inner 결과는 일곱 행이었다. A의 l1과 l2는 각각 r1·r2·r3에 연결되고 B에서 한 조합이 생겼다. 이 곱셈은 같은 단일 비결측 키를 등호로 결합한 예의 설명이다. 복합 키라면 전체 키 조합별로 세어야 하며, 범위·날짜 조건을 추가한 결합에 이 숫자를 그대로 적용하지 않는다.

양쪽 미매칭도 남기는 outer 결합에서는 여기에 L과 R이 각각 한 행씩 더해져 아홉 행이 됐다. 행 수만 보면 정상 결합과 누락·확대가 뒤섞이므로 어떤 키에서 늘었고 어떤 키가 연결되지 않았는지 나누어 본다.

먼저 기대한 관계를 문장으로 적는다

‘고객 목록의 고객 한 명은 한 행이고, 처리 이력은 고객당 여러 행이다’라면 고객→이력은 일대다다. 반대로 이력마다 고객 이름 하나만 붙이려면 이력→고객은 다대일이며 고객 표의 키가 유일해야 한다. 같은 데이터라도 어느 쪽을 왼쪽에 두었는지에 따라 검증 방향이 달라진다.

같은 평가 카드가 청록색과 노란색 두 목록으로 나뉘고 돋보기로 실패 카드를 살펴보는 삽화
같은 입력의 두 결과를 대조하는 기존 AI 생성 개념도. 결합 전후 같은 입력 행이 어떤 결과에 대응하는지 확인하는 맥락으로 사용했다. 실제 데이터베이스 화면이나 결합 행수를 그린 도표는 아니다.

키가 중복돼 보이면 날짜·사업장·계정처럼 식별 범위를 빠뜨렸는지도 본다. 실제로는 ‘사업장+고객번호’가 키인데 고객번호만 사용하면 다른 대상을 연결할 수 있다. 반대로 원래 여러 이력을 갖는 정상 키를 억지로 한 행으로 줄이면 정보가 사라진다. 식별자의 선행 0과 정밀도가 중간 변환에서 바뀌지 않았는지도 별도로 확인한다.

validate는 기대 관계를 검사하고 indicator는 연결 상태를 보여 준다

DataFrame.merge 공식 문서의 validate에서 one_to_one은 양쪽 키의 유일성, one_to_many는 왼쪽, many_to_one은 오른쪽 키의 유일성을 검사한다. many_to_many는 다대다를 허용하지만 유일성 검사를 수행하지 않는다. ‘many_to_many로 실행됐으니 업무 관계가 검증됐다’는 해석은 맞지 않는다.

import pandas as pd

left = pd.DataFrame({'key': ['A', 'A', 'B', 'L']})
right = pd.DataFrame({'key': ['A', 'A', 'A', 'B', 'R']})

joined = left.merge(right, on='key', how='outer', indicator=True)
joined['_merge'].value_counts()
# both 7, left_only 1, right_only 1

# 이 입력은 오른쪽 키도 중복돼 many_to_one 검사를 통과하지 못한다.
# left.merge(right, on='key', validate='many_to_one')

로컬에서 one_to_one과 many_to_one을 각각 실행하자 MergeError가 났다. 오른쪽을 A·B가 한 번씩 있는 별도 조회 표로 바꿔 왼쪽 결합을 하면 many_to_one 검사를 통과하고 왼쪽 네 행이 유지됐으며 L은 미매칭이었다. 이것은 입력을 지워 오류를 숨긴 수습이 아니라 서로 다른 관계 계약의 합성 비교다.

indicator의 both는 키가 양쪽에 나타났다는 뜻이지, 이름·금액까지 맞거나 한 번만 연결됐다는 뜻이 아니다. 왼쪽 결합에는 오른쪽에만 있는 R이 결과로 나오지 않으므로 오른쪽 누락까지 조사하려면 별도 목록 대조나 outer 결과가 필요하다. 이 확인 방식은 작성자의 점검 제안이다.

결과에서 중복을 지우기 전에 원래 행을 추적한다

drop_duplicates로 같은 키만 남기면 서로 다른 처리 이력이나 주문 항목까지 버릴 수 있다. 어느 입력 행이 어느 상대 행과 연결됐는지 ID를 잠시 함께 두고, 오류가 키 범위 누락인지·조회 표의 잘못된 중복인지·의도한 이력 확장인지 판단한다. 삭제가 필요하다면 먼저 실제 중복의 정의와 보존할 행의 기준이 있어야 한다.

결합 전에 표가 이미 틀어졌다면 CSV의 레코드·필드 파싱부터 돌아본다. 입력을 올바로 읽었어도 빈 키는 도구에 따라 다르게 연결될 수 있으므로 SQL NULL과 pandas 결측 키의 매칭 차이를 따로 확인한다. 늘어난 행에서 금액을 더할 때의 문제는 두 자식 표의 중복 합계와 사전 집계로 이어진다.

자주 묻는 질문

왼쪽 결합이면 왼쪽 행 수가 그대로인가요?

오른쪽에 같은 키가 여러 번 있으면 왼쪽 한 행이 여러 행으로 늘 수 있다. 왼쪽 행마다 상대가 최대 하나라는 계약이라면 오른쪽 키 유일성을 검사하고, 실제 결과에서도 입력 ID별 연결 수를 확인한다.

양쪽에 중복 키가 있으면 무조건 잘못된 표인가요?

아니다. 각각 다른 사건·항목을 표현하는 정상 행일 수 있다. 필요한 결과가 모든 조합인지, 더 구체적인 키로 연결해야 하는지, 먼저 집계할 자료인지 업무 단위를 보고 정한다.

validate를 통과하면 결합 내용도 정확한가요?

검사한 방향의 키 유일성 조건을 만족했다는 뜻이다. 다른 대상의 키를 선택했거나 값이 잘못 변환됐거나 미매칭이 남는 문제까지 해결하지 않는다. 값의 의미와 연결 범위는 별도 대조가 필요하다.

이 카테고리의 다른 글

전체글 보기

“두 표를 합쳤더니 행이 늘었다: JOIN 키의 중복과 관계 확인”에 대한 3개의 생각

댓글 남기기