0Pricing
Coding Interview Prep · 강의

JOIN으로 집합 연산 흉내 내기

INTERSECT와 EXCEPT가 없는 방언에서 이를 JOIN으로 다시 작성합니다.

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

집합 연산을 흉내 내는 이유

모든 데이터베이스가 INTERSECT와 EXCEPT를 지원하는 것은 아닙니다. 예를 들어 이전 버전의 MySQL은 이 연산자들을 전혀 지원하지 않았습니다. 면접관은 연산자를 사용할 수 없을 때 조인과 하위 쿼리로 집합 논리를 재현할 수 있는지 확인합니다.

집합 연산자와 이를 조인으로 구현한 방식을 모두 알고 있으면 해당 연산자가 실제로 무엇을 계산하는지 이해하고 있다는 것을 보여 줄 수 있습니다.

INNER JOIN으로 INTERSECT 구현하기

INTERSECT는 두 집합에 공통으로 있는 행을 찾습니다. 조인으로 구현하려면 비교하는 모든 열을 기준으로 INNER JOIN을 수행하고, 중복 제거 동작을 맞추기 위해 DISTINCT를 추가합니다.

비교 대상인 모든 열은 조인 조건의 일부가 됩니다.

-- A INTERSECT B emulated:
SELECT DISTINCT a.customer_id
FROM orders_2023 a
JOIN orders_2024 b
  ON a.customer_id = b.customer_id;

INTERSECT에 DISTINCT가 필요한 이유

일반적인 INNER JOIN은 행을 여러 개로 늘릴 수 있습니다. 어느 한쪽에 값이 여러 번 나타나면 조인이 행을 곱셈처럼 증가시키기 때문입니다. 표준 INTERSECT는 각 공통 행을 한 번씩만 반환하므로, 조인이 만든 중복을 합치려면 DISTINCT를 추가해야 합니다.

여기서 DISTINCT를 잊는 것은 면접에서 흔히 하는 실수입니다.

-- without DISTINCT, a customer with 3 orders in each year
-- would appear 9 times from the join

EXCEPT를 LEFT JOIN / IS NULL로 구현하기

EXCEPT(A에는 있지만 B에는 없는 것)는 안티 조인입니다. 이식성 높은 방식은 모든 열을 기준으로 A에서 B로 LEFT JOIN하고, B 쪽이 NULL인 행(일치하는 행이 없는 경우)만 남긴 다음 DISTINCT를 적용하는 것입니다.

이 LEFT JOIN / IS NULL 패턴은 SQL 면접에서 가장 자주 재사용되는 요령 중 하나입니다.

SELECT DISTINCT a.customer_id
FROM orders_2023 a
LEFT JOIN orders_2024 b
  ON a.customer_id = b.customer_id
WHERE b.customer_id IS NULL;

EXCEPT와 NOT EXISTS

이식성 높은 또 다른 EXCEPT 구현에는 NOT EXISTS를 사용합니다. 이는 "일치하는 B 행이 존재하지 않는 각 A 행을 남긴다"는 의미이며 NULL도 안정적으로 처리합니다.

많은 엔지니어가 NOT EXISTS를 선호합니다. 의도가 명확하게 드러나고 NOT IN + NULL 함정을 피할 수 있기 때문입니다.

SELECT DISTINCT a.customer_id
FROM orders_2023 a
WHERE NOT EXISTS (
  SELECT 1 FROM orders_2024 b
  WHERE b.customer_id = a.customer_id
);

INTERSECT와 EXISTS

대칭적으로 INTERSECT는 EXISTS를 사용해 작성할 수 있습니다. 일치하는 B 행이 존재하는 각 중복 없는 A 행을 남기는 방식입니다.

EXISTS는 첫 번째 일치 항목을 찾으면 검색을 중단하므로 효율적일 수 있으며, 조인으로 인한 행 증가를 피합니다. 따라서 조인 쪽에서 DISTINCT가 필요하지 않을 때도 있습니다.

SELECT DISTINCT a.customer_id
FROM orders_2023 a
WHERE EXISTS (
  SELECT 1 FROM orders_2024 b
  WHERE b.customer_id = a.customer_id
);

NOT IN의 NULL 함정

EXCEPT를 흉내 내는 방법으로 NOT IN을 사용하고 싶을 수 있지만 위험합니다. 하위 쿼리가 NULL을 하나라도 반환하면 비교 결과가 UNKNOWN이 되므로 NOT IN은 행을 전혀 반환하지 않습니다.

이는 매우 자주 출제되는 함정입니다. NULL을 안전하게 처리하는 NOT EXISTS 또는 LEFT JOIN / IS NULL을 사용하십시오.

-- RISKY if orders_2024.customer_id can be NULL:
SELECT DISTINCT customer_id FROM orders_2023
WHERE customer_id NOT IN (
  SELECT customer_id FROM orders_2024
);

여러 열을 기준으로 일치시키기

집합 비교가 여러 열에 걸쳐 있다면 모든 열을 술어에 포함해 조인해야 합니다. 안티 조인의 경우 해당 열에 NULL이 있을 가능성도 처리해야 하며, 이때 NOT EXISTS가 특히 유용합니다.

ON 절에 각 열을 명시하십시오. 하나라도 빠뜨리면 "같은 행"의 의미가 조용히 달라집니다.

SELECT DISTINCT a.id, a.city
FROM a
LEFT JOIN b
  ON a.id = b.id AND a.city = b.city
WHERE b.id IS NULL;

연산자 없이 UNION 구현하기

UNION ALL은 단순한 연결이므로 모든 SQL 방언에서 직접 지원합니다. 중복 제거가 필요한 UNION을 구현하려면 하위 쿼리 안에서 UNION ALL로 연결한 뒤, 모든 열에 대해 SELECT DISTINCT 또는 GROUP BY를 적용해 감싸면 됩니다.

이를 통해 UNION이 단순히 UNION ALL에 중복 제거 단계를 더한 것임을 알 수 있습니다.

SELECT DISTINCT * FROM (
  SELECT city FROM a
  UNION ALL
  SELECT city FROM b
) combined;

적절한 구현 방식 선택하기

선택 안내:

  • INTERSECT → EXISTS 또는 INNER JOIN + DISTINCT
  • EXCEPT → NOT EXISTS 또는 LEFT JOIN / IS NULL
  • NULL이 있을 수 있다면 NOT IN을 피하십시오.
  • UNION → DISTINCT로 감싼 UNION ALL

EXISTS / NOT EXISTS는 이식성이 가장 높고 NULL도 안전하게 처리하므로 가장 안전한 면접 답변입니다.

전체 내용 연결하기

집합 연산자를 조인으로 바꿔 쓸 수 있다는 것은 단순한 문법이 아니라 집합 논리로 이해하고 있음을 보여 줍니다. 안티 조인(LEFT JOIN / IS NULL 또는 NOT EXISTS)은 가장 중요한 패턴입니다. EXCEPT 구현, 고아 레코드 찾기, 누락 레코드 문제에 모두 등장하기 때문입니다.

정확성을 위해 먼저 NOT EXISTS를 제시한 다음, 성능 논의를 위해 조인 방식도 언급하십시오.

빠른 확인

사용 중인 데이터베이스는 EXCEPT를 지원하지 않습니다. orders_2023에는 있지만 orders_2024에는 없는 customer_ids가 필요하며, 이 열에는 NULL이 포함될 수 있습니다.

요약

핵심 요점:

  • INTERSECT → INNER JOIN + DISTINCT 또는 EXISTS
  • EXCEPT → LEFT JOIN / IS NULL 또는 NOT EXISTS(안티 조인)
  • 집합 연산자의 중복 제거 동작을 재현하고 조인으로 인한 행 증가를 줄이려면 DISTINCT를 추가하십시오.
  • NULL이 있을 수 있을 때는 NOT IN을 피하고 NOT EXISTS를 사용하십시오.
  • UNION = DISTINCT로 감싼 UNION ALL

자주 묻는 질문

“JOIN으로 집합 연산 흉내 내기” 강의는 무료인가요?

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

“JOIN으로 집합 연산 흉내 내기”에서 뭘 배우나요?

INTERSECT와 EXCEPT가 없는 방언에서 이를 JOIN으로 다시 작성합니다. 브라우저에서 직접 실행하는 실습 코드로 Coding Interview Prep을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.

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

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

“JOIN으로 집합 연산 흉내 내기” 강의는 얼마나 걸리나요?

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

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

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

이 강의의 모든 강의

  1. UNION과 UNION ALL 비교하기
  2. 열 개수와 형식 호환성
  3. 비교를 위한 INTERSECT와 EXCEPT
  4. JOIN으로 집합 연산 흉내 내기
← Coding Interview Prep(으)로 돌아가기