0Pricing
Coding Interview Prep · 강의

안전하게 행 중복 제거하기

완전히 같거나 유사한 중복 행을 제거하면서 대표 레코드 하나를 유지합니다.

안전하게 행 중복 제거하기은(는) CoddyKit의 무료 Coding Interview Prep 강의입니다. 이것은 4개 중 3번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 Coding Interview Prep 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. Coding Interview Prep 강의에는 총 4개의 강의가 포함되어 있습니다.

중복 제거 문제

“이 테이블에는 중복 행이 있습니다. 중복을 제거하되 각 행은 하나씩 남기십시오.” 거의 모든 데이터 엔지니어링 면접에는 이와 비슷한 문제가 등장합니다. 문제는 이를 안전하게 처리하는 것입니다. 즉, 정확히 하나의 기준 행만 남기고 단지 비슷해 보일 뿐인 서로 다른 기록을 실수로 삭제하지 않아야 합니다.

중복 행을 탐지하고, 어떤 복사본을 남길지 선택하며, SELECT에서 중복을 제거하고, 테이블에서 중복 행을 실제로 삭제하는 방법을 다룹니다.

먼저 중복 정의하기

면접관에게 가장 먼저 물어야 할 질문은 다음과 같습니다. “두 행을 중복으로 판단하는 기준은 무엇인가요?” 가능한 기준은 다음과 같습니다.

  • 완전히 동일한 중복: 모든 열이 동일합니다.
  • 키 중복: 동일한 업무 키를 가지지만(예: 동일한 email), 다른 열의 값은 다를 수 있습니다.

각 경우에 사용하는 기법은 다릅니다. 절대 추측하지 마십시오. 중복의 정의를 명확히 하는 것이 가장 중요한 단계이며, 면접관은 지원자가 이 질문을 하기를 기대합니다.

중복 탐지

중복 키를 찾으려면 중복을 정의하는 열을 기준으로 그룹을 묶고, 개수가 1보다 큰 그룹만 남기십시오. 이렇게 하면 변경하기 전에 영향을 받는 키와 각 키가 몇 번씩 존재하는지 확인할 수 있습니다.

먼저 탐지 질의를 실행하는 것은 반드시 설명할 만한 모범 사례입니다. 삭제하기 전에 문제의 규모를 확인할 수 있기 때문입니다.

SELECT email, COUNT(*) AS copies
FROM users
GROUP BY email
HAVING COUNT(*) > 1
ORDER BY copies DESC;

완전히 동일한 중복: DISTINCT

모든 열의 값이 실제로 동일한 중복이라면, 읽기 전용 중복 제거 뷰는 SELECT DISTINCT *만큼 간단합니다. UNION(ALL 없이 사용)도 중복 행을 제거합니다.

하지만 DISTINCT는 전체 행의 중복을 제거하고 어떤 복사본을 남길지 선택할 필요가 없을 때만 유용합니다. 키를 기준으로 중복을 제거하면서 열의 값이 서로 다르다면 순위 지정이 필요합니다.

-- Read-only dedup of exact-duplicate rows
SELECT DISTINCT customer_id, name, signup_date
FROM customers;

키 중복: ROW_NUMBER

행이 같은 키를 공유하지만 다른 열의 값이 다르다면, 키를 기준으로 파티션을 나누고 각 복사본에 번호를 매기십시오. rn = 1은 남길 행을 나타내고, rn > 1은 삭제할 추가 행을 나타냅니다.

윈도 안의 ORDER BY가 어떤 복사본을 기준 행으로 삼을지 결정합니다. 예를 들어 가장 최근에 갱신된 행을 남기는 식으로, 이 순서를 신중하게 선택하십시오.

SELECT *,
  ROW_NUMBER() OVER (
    PARTITION BY email
    ORDER BY updated_at DESC
  ) AS rn
FROM users;

기준 복사본 선택

번호 매기기를 CTE로 감싸고 rn = 1인 행만 남기십시오. 그러면 키마다 하나의 행, 즉 ORDER BY에서 첫 번째로 순위가 매겨진 행이 반환됩니다.

이 SELECT 형식은 원본을 변경하지 않습니다. 따라서 원본에 손대지 않고 정리된 뷰를 만들거나, 중복이 제거된 대상 테이블에 INSERT ... SELECT로 데이터를 입력할 때 적합합니다.

WITH ranked AS (
  SELECT *,
    ROW_NUMBER() OVER (
      PARTITION BY email ORDER BY updated_at DESC
    ) AS rn
  FROM users
)
SELECT user_id, email, name, updated_at
FROM ranked
WHERE rn = 1;

정렬 순서가 중요한 이유

파티션 내부의 ORDER BY는 형식적인 절차가 아니라 업무상 결정입니다.

  • ORDER BY updated_at DESC는 가장 최근에 갱신된 기록을 남깁니다.
  • ORDER BY created_at ASC는 원본 기록을 남깁니다.
  • ORDER BY id ASC는 가장 낮은 대리 키를 남기므로, 안정적인 임의 선택이 필요할 때 유용합니다.

기본 정렬 열도 동률일 때 선택되는 행이 항상 같도록 고유한 동률 해소 기준을 추가하십시오.

ROW_NUMBER() OVER (
  PARTITION BY email
  ORDER BY updated_at DESC, id ASC
) AS rn

중복 행 실제 삭제

테이블에서 중복 행을 실제로 제거하려면 추가 행(rn > 1)을 식별한 다음 삭제하십시오. PostgreSQL과 SQL 서버에서는 CTE를 사용해 삭제할 수 있고, MySQL에서는 자기 조인이나 하위 질의를 자주 사용합니다.

항상 먼저 일치하는 SELECT를 실행하여 정확히 어떤 행이 사라질지 미리 확인하십시오. 확인 없이 삭제하는 것이 바로 이 질문에서 탈락하는 이유입니다.

WITH ranked AS (
  SELECT ctid,
    ROW_NUMBER() OVER (
      PARTITION BY email ORDER BY updated_at DESC, id ASC
    ) AS rn
  FROM users
)
DELETE FROM users
WHERE ctid IN (SELECT ctid FROM ranked WHERE rn > 1);

자기 조인 삭제 패턴

이식성 높은 고전적 접근법은 중복 키마다 가장 작은 id를 가진 행을 남기고, 자기 조인을 사용해 나머지를 삭제합니다. 윈도 함수를 사용할 필요가 없으므로 오래된 데이터베이스 엔진에서도 유용합니다.

조인 조건은 각 행을 같은 키를 가지면서 더 작은 id를 가진 다른 행과 연결합니다. 따라서 더 작은 id를 가진 쌍이 존재하는 모든 행이 삭제할 중복 행이 됩니다.

DELETE u1
FROM users u1
JOIN users u2
  ON u1.email = u2.email
 AND u1.id > u2.id;

안전성 점검 목록

삭제하기 전에 다음과 같이 안전장치를 마련하십시오.

  • 삭제를 트랜잭션으로 감싸 개수가 잘못된 것 같을 때 ROLLBACK할 수 있게 하십시오.
  • 먼저 삭제 대상 행에 대해 SELECT COUNT(*)를 실행하고 결과가 타당한지 확인하십시오.
  • 백업 테이블을 만드는 것도 고려하십시오. 예: CREATE TABLE users_bak AS SELECT * FROM users.
  • PARTITION BY 열이 실제로 중복을 정의하는지 확인하십시오. 그렇지 않으면 서로 다른 기록을 삭제할 수 있습니다.
BEGIN;
-- run the DELETE, inspect row count
-- COMMIT; if correct, otherwise ROLLBACK;

유사 중복과 정규화

행이 완전히 같지는 않지만 논리적으로는 같은 경우도 있습니다. 예를 들어 'Ann@X.com'과 'ann@x.com'처럼 대소문자가 다르거나 뒤에 공백이 붙은 경우입니다. 원시 열이 아니라 정규화된 식을 기준으로 파티션을 나누십시오.

정규화를 언급하면 실무 감각을 보여 줄 수 있습니다. 실제 데이터의 중복은 대소문자, 공백 또는 형식의 차이 뒤에 숨어 있는 경우가 많아 단순한 키 비교로는 놓칠 수 있기 때문입니다.

ROW_NUMBER() OVER (
  PARTITION BY LOWER(TRIM(email))
  ORDER BY updated_at DESC, id ASC
) AS rn

빠른 확인

안전한 중복 제거 방법을 선택하십시오.

복습: 안전한 중복 제거

중복은 체계적으로 제거하십시오.

  • 먼저 중복의 의미를 정의한 다음 GROUP BY / HAVING COUNT(*) > 1로 탐지하십시오.
  • 완전히 동일한 중복 → DISTINCT. 키 중복 → 키를 기준으로 파티션을 나눈 ROW_NUMBER를 사용하고 rn = 1을 남깁니다.
  • 윈도의 ORDER BY가 기준 복사본을 선택하므로, 고유한 동률 해소 기준을 추가하십시오.
  • 개수를 미리 확인한 뒤 트랜잭션 안에서 rn > 1인 행을 삭제하십시오.
  • 유사 중복도 찾을 수 있도록 키를 정규화하십시오.

자주 묻는 질문

“안전하게 행 중복 제거하기” 강의는 무료인가요?

네 — “안전하게 행 중복 제거하기” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 Coding Interview Prep 강의 전체를 잠금 해제할 수 있습니다. Coding Interview Prep 강의에는 총 4개의 강의가 포함되어 있습니다.

“안전하게 행 중복 제거하기”에서 뭘 배우나요?

완전히 같거나 유사한 중복 행을 제거하면서 대표 레코드 하나를 유지합니다. 브라우저에서 직접 실행하는 실습 코드로 Coding Interview Prep을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.

Coding Interview Prep을(를) 시작하는 데 경험이 필요한가요?

사전 경험은 필요하지 않습니다. CoddyKit의 Coding Interview Prep은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 3번째 강의입니다.

“안전하게 행 중복 제거하기” 강의는 얼마나 걸리나요?

대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.

이 Coding Interview Prep 강의에서 코드를 작성하고 실행할 수 있나요?

네. 모든 Coding Interview Prep 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.

이 강의의 모든 강의

  1. ROW_NUMBER로 그룹별 상위 N개 행 찾기
  2. 상위 N개 결과에서 동점 처리하기
  3. 안전하게 행 중복 제거하기
  4. 키별 최신 행 유지하기
← Coding Interview Prep(으)로 돌아가기