테이블 A에는 있지만 테이블 B에는 존재하지 않는 데이터를 조회하고 싶다면 LEFT JOIN을 활용하는 것이 가장 일반적이고 효율적인 방법입니다. LEFT JOIN으로 두 테이블을 연결한 뒤, WHERE 절에서 테이블 B의 컬럼이 NULL인 행만 필터링하면 두 테이블 간의 차집합을 손쉽게 구할 수 있습니다.
기본 문법
select yourTableNameA.* from yourTableNameA left join yourTableNameB on yourTableNameA.yourColumnName = yourTableNameB.yourColumnName where yourTableNameB.yourColumnName IS NULL;
동작 원리를 쉽게 이해하기 위해 실제 예제를 통해 단계별로 살펴보겠습니다.
1단계: table_A 생성 및 데이터 입력
먼저 비교 기준이 되는 첫 번째 테이블인 table_A를 생성합니다.
<table_A>
mysql> create table table_A -> ( -> Value int -> ); Query OK, 0 rows affected (1.10 sec)
INSERT 명령어를 사용해 table_A에 레코드를 추가합니다.
mysql> insert into table_A values(10); Query OK, 1 row affected (0.32 sec) mysql> insert into table_A values(15); Query OK, 1 row affected (0.10 sec) mysql> insert into table_A values(35); Query OK, 1 row affected (0.21 sec) mysql> insert into table_A values(45); Query OK, 1 row affected (0.13 sec)
SELECT 문으로 table_A의 전체 레코드를 확인합니다.
mysql> select *from table_A;
실행 결과는 다음과 같습니다.
+-------+ | Value | +-------+ | 10 | | 15 | | 35 | | 45 | +-------+ 4 rows in set (0.00 sec)
2단계: table_B 생성 및 데이터 입력
이번에는 비교 대상이 될 두 번째 테이블 table_B를 생성합니다.
<table_B>
mysql> create table table_B -> ( -> Value int -> ); Query OK, 0 rows affected (0.51 sec)
마찬가지로 INSERT 명령어로 table_B에 레코드를 입력합니다.
mysql> insert into table_B values(10); Query OK, 1 row affected (0.18 sec) mysql> insert into table_B values(20); Query OK, 1 row affected (0.12 sec) mysql> insert into table_B values(35); Query OK, 1 row affected (0.23 sec) mysql> insert into table_B values(60); Query OK, 1 row affected (0.12 sec)
SELECT 문으로 table_B의 전체 레코드를 확인합니다.
mysql> select *from table_B;
실행 결과는 다음과 같습니다.
+-------+ | Value | +-------+ | 10 | | 20 | | 35 | | 60 | +-------+ 4 rows in set (0.00 sec)
3단계: LEFT JOIN으로 차집합 조회하기
두 테이블이 모두 준비되었습니다. 현재 table_A에는 10, 15, 35, 45가 있고, table_B에는 10, 20, 35, 60이 있습니다. 이제 LEFT JOIN을 사용해 table_B에 존재하지 않는 table_A의 값을 조회해 보겠습니다.
mysql> select table_A.* -> from table_A left join table_B on table_A.Value = table_B.Value -> where table_B.Value IS NULL;
실행 결과는 다음과 같습니다.
+-------+ | Value | +-------+ | 15 | | 45 | +-------+ 2 rows in set (0.04 sec)
결과 분석
table_A의 값 중 10과 35는 table_B에도 존재하므로 결과에서 제외되었고, table_B에 없는 15와 45만 최종적으로 반환되었습니다. 즉, LEFT JOIN으로 양쪽 테이블을 연결한 상태에서 table_B 쪽 값이 NULL인 행만 골라낸 것입니다.
참고로 MySQL은 표준 SQL의 MINUS나 EXCEPT 연산자를 지원하지 않기 때문에, 이처럼 차집합을 구할 때는 LEFT JOIN ... IS NULL 패턴 외에도 NOT IN 또는 NOT EXISTS 서브쿼리를 활용할 수 있습니다. 그중에서도 LEFT JOIN 방식은 조인 조건에 인덱스를 잘 활용할 수 있어 대용량 데이터 처리 시 성능 면에서 유리한 경우가 많습니다.