Devin.KR

조인 결과 예측 연습 - 행 수를 먼저 세기

개발자KR 조회 11

이 장에서 배우는 것

조인 쿼리는 짜고 나서 실행해 보기 전까지 결과 행 수를 가늠하기 어렵다. 그런데 실무에서 사고가 나는 지점은 대개 문법 오류가 아니라 "이 조인이 몇 행을 만들어내는가"를 예측하지 못한 채 실행했을 때다. 이 장에서는 앞 장에서 다룬 NULL 비교의 함정을 조인 상황으로 확장해, 조인을 실행하기 전에 결과 행 수를 손으로 계산하는 습관을 만든다.

  • INNER JOIN, LEFT JOIN, FULL OUTER JOIN이 각각 몇 행을 만들어내는지 손으로 계산할 수 있다.
  • 같은 조건이라도 ON 절과 WHERE 절 중 어디에 두느냐에 따라 결과가 달라지는 이유를 NULL 비교로 설명할 수 있다.
  • 셀프 조인에서 비교 연산자(=, <, <>) 선택에 따라 결과 행 수가 어떻게 바뀌는지 계산할 수 있다.
  • 조인 조건을 빠뜨렸을 때 생기는 카티션 곱(cartesian product)을 실행 전에 알아챌 수 있다.
  • MySQL 8에는 없는 FULL OUTER JOIN을 표준 SQL 방식으로 대체하는 방법을 익힌다.

문제 상황

온라인 서점 마케팅팀이 "2월에 주문한 회원과 주문 내역을 회원 전체 목록 기준으로 보여 달라"고 요청했다. 개발자는 회원 테이블을 기준으로 주문 테이블을 LEFT JOIN 한 뒤, 2월 주문만 걸러내려고 WHERE 절에 주문일자 >= '2026-02-01' 조건을 추가했다. 그런데 결과를 확인해 보니 2월에 주문하지 않은 회원은 물론이고, 애초에 주문 이력이 전혀 없는 회원까지 목록에서 통째로 사라졌다. LEFT JOIN을 썼는데도 결과가 INNER JOIN처럼 나온 것이다.

이 문제는 쿼리를 실행해 본 다음에야 발견됐다. 조인을 실행하기 전에 "이 조인은 몇 행이 나와야 정상인가"를 먼저 계산해 두었다면, 결과 행 수가 예상보다 적게 나온 순간 바로 원인을 의심할 수 있었을 것이다. 이 장에서는 이런 사고를 예측 가능한 계산으로 바꾸는 방법을 다룬다.

INNER·LEFT·FULL OUTER 조인의 행 수 계산

조인 결과 행 수를 예측하는 기본 원리는 하나다. 왼쪽(또는 기준) 테이블의 한 행마다, 그 행과 매칭되는 오른쪽 행이 몇 개인지 세어서 모두 더한다. INNER JOIN, LEFT JOIN, FULL OUTER JOIN은 이 합계에 "매칭되지 않은 행을 포함할 것인가"만 다르게 적용한다.

주문 테이블에는 4건의 주문이 있고, 주문상세 테이블에는 각 주문에 딸린 도서 행이 들어 있다. 주문 101은 2권, 102는 1권, 103은 1권, 104는 2권을 담고 있다. 이 값을 미리 세어 두면 INNER JOIN 결과 행 수는 각 주문의 매칭 건수를 더한 값과 정확히 일치해야 한다.

주문 4건과 주문상세를 조인하면 매칭 건수의 합인 6행이 나온다는 것을 보여주는 그림

여기에 도서 테이블을 한 번 더 INNER JOIN 해도 행 수는 그대로 6행이다. 주문상세에 들어 있는 도서번호(B01~B04)가 모두 도서 테이블에 존재하기 때문에, 추가 조인이 행을 늘리거나 줄이지 않는다. 반대로 만약 주문상세에 도서 테이블에 없는 도서번호가 있었다면 INNER JOIN에서 그 행은 사라졌을 것이다.

LEFT JOIN은 여기에 "매칭이 하나도 없어도 왼쪽 행은 보존한다"는 규칙이 추가된다. 매칭이 없으면 오른쪽 컬럼은 모두 NULL로 채워진 행 1개가 대신 들어간다. 회원 4명을 기준으로 주문을 LEFT JOIN 하면, 주문이 없는 회원도 NULL이 채워진 1행으로 남는다.

MySQL과 Oracle의 FULL OUTER 구문 차이

FULL OUTER JOIN은 "양쪽 중 어느 한쪽에라도 있으면 포함한다"는 조인이다. 문제는 MySQL 8에는 FULL OUTER JOIN 문법 자체가 없다는 점이다. Oracle은 FULL OUTER JOIN을 그대로 쓸 수 있지만, MySQL에서는 LEFT JOIN 결과와 RIGHT JOIN 결과를 UNION으로 합쳐서 흉내 내야 한다.

FULL OUTER JOIN 관련 MySQL 8과 Oracle의 문법 차이
기능MySQL 8Oracle비고
FULL OUTER JOIN지원하지 않음지원함MySQL은 LEFT JOIN과 RIGHT JOIN을 UNION으로 합쳐 대체한다
구식 외부 조인 표기 (+)지원하지 않음지원하지만 비권장두 벤더 모두 표준 LEFT/RIGHT JOIN 사용을 권장한다
콤마 조인(FROM a, b)지원함지원함WHERE 조건 없이도 실행되어 카티션 곱 위험이 있다
WITH 절(CTE)지원함(8.0 이상)지원함FULL OUTER 대체 쿼리 작성에 함께 사용한다

1월에 팔린 도서 목록과 2월에 팔린 도서 목록을 비교하는 경우를 생각해 보자. 두 집합은 서로의 부분집합이 아니므로, 어느 한쪽에만 있는 도서와 양쪽에 모두 있는 도서를 한 번에 보려면 FULL OUTER 개념이 필요하다. 1월에는 B01, B02가 팔렸고 2월에는 B01, B03, B04가 팔렸다면, 결과는 B01(양쪽), B02(1월만), B03(2월만), B04(2월만) 총 4행이어야 한다.

조인 종류별로 결과에 포함되는 행의 규칙
조인 종류결과에 포함되는 행이 장 예시의 행 수주의점
INNER JOIN양쪽 조건이 모두 일치하는 행만주문+주문상세+도서 = 6행한쪽에만 있는 데이터는 결과에서 사라진다
LEFT JOIN왼쪽 전체 + 일치하는 오른쪽(없으면 NULL)회원 LEFT JOIN 주문(2월 조건, ON) = 4행필터를 WHERE에 두면 LEFT의 의미가 사라진다
FULL OUTER(대체)양쪽 중 어느 한쪽이라도 있으면 포함1월/2월 판매 도서 비교 = 4행MySQL은 LEFT UNION RIGHT로 직접 구현해야 한다
콤마 조인(조건 누락)두 집합의 모든 조합(카티션 곱)주문 4행 × 주문상세 6행 = 24행의도한 조인이 아니라 조건 누락 실수일 가능성이 높다

ON 절과 WHERE 절, 조건을 어디에 두느냐의 차이

INNER JOIN에서는 ON 절 조건과 WHERE 절 조건이 결과에 미치는 영향이 사실상 같다. 문제는 LEFT JOIN이다. LEFT JOIN은 오른쪽 테이블과 매칭이 없어도 왼쪽 행을 NULL로 채워 보존하는데, 이 NULL이 채워진 행에 WHERE 절 조건을 걸면 NULL >= '2026-02-01' 같은 비교가 UNKNOWN이 되어 행 자체가 사라진다. 결국 LEFT JOIN을 쓴 의미가 없어진다.

회원 4명을 기준으로 2월 주문만 보고 싶을 때, 조건을 ON 절에 두면 조인이 성립되는 단계에서만 필터가 적용되므로 회원 4명이 모두 남는다. 반대로 같은 조건을 WHERE 절로 옮기면, 조인이 끝난 뒤 최종 결과에서 필터링되기 때문에 2월 주문이 없는 회원의 행(NULL로 채워진 행 포함)이 전부 날아간다.

조건을 ON에 두면 4행이 유지되고 WHERE로 옮기면 2행만 남는다는 것을 보여주는 그림
ON 절과 WHERE 절 조건 위치에 따른 차이
조건 위치필터가 적용되는 시점이 장 예시의 결과 행 수
ON 절조인 매칭 여부를 정할 때(조인 전)4행 — 모든 회원 유지, 조건 불일치 시 NULL
WHERE 절조인이 끝난 결과에서 걸러낼 때(조인 후)2행 — NULL 비교가 UNKNOWN이 되어 두 회원이 사라짐

셀프 조인과 카티션 곱 실수

셀프 조인으로 같은 테이블에서 짝 찾기

셀프 조인(self join)은 같은 테이블을 두 개의 별칭으로 나눠서 자기 자신과 조인하는 방식이다. "같은 책을 리뷰한 서로 다른 회원 쌍을 찾는다" 같은 요구는 리뷰 테이블 하나만으로 해결할 수 있다. 이때 행 수를 예측하려면 조인 조건에 쓰인 비교 연산자를 정확히 봐야 한다.

리뷰 4건 중 B01 책은 회원 1과 회원 2가 각각 리뷰를 남겼다. r1.도서번호 = r2.도서번호만 걸면 회원 1과 자기 자신, 회원 2와 자기 자신까지 포함되어 불필요한 행이 생긴다. 여기에 r1.회원번호 < r2.회원번호 조건을 더하면 자기 자신과의 짝이 제거되고, (1,2)와 (2,1)처럼 방향만 다른 중복 쌍도 한 방향으로만 남는다.

조인 조건을 빠뜨렸을 때 생기는 카티션 곱

FROM 절에 테이블을 콤마로 나열하고 조인 조건을 WHERE에 넣는 것을 잊으면, 두 테이블의 모든 행 조합이 만들어진다. 주문 4행과 주문상세 6행을 조건 없이 나열하면 4 × 6 = 24행이 나온다. 문법 오류가 아니기 때문에 쿼리는 정상적으로 실행되고, 행 수가 비정상적으로 많다는 것을 직접 확인하지 않으면 실수를 알아채기 어렵다.

완성 코드

-- 스키마: 온라인 서점 (회원, 도서, 주문, 주문상세, 리뷰)
CREATE TABLE 회원 (
  회원번호 INT PRIMARY KEY,
  이름     VARCHAR(20) NOT NULL,
  등급     VARCHAR(10) NOT NULL
);

CREATE TABLE 도서 (
  도서번호 VARCHAR(10) PRIMARY KEY,
  제목     VARCHAR(50) NOT NULL,
  가격     INT NOT NULL
);

CREATE TABLE 주문 (
  주문번호 INT PRIMARY KEY,
  회원번호 INT NOT NULL,
  주문일자 DATE NOT NULL,
  FOREIGN KEY (회원번호) REFERENCES 회원 (회원번호)
);

CREATE TABLE 주문상세 (
  주문번호 INT NOT NULL,
  도서번호 VARCHAR(10) NOT NULL,
  수량     INT NOT NULL,
  PRIMARY KEY (주문번호, 도서번호),
  FOREIGN KEY (주문번호) REFERENCES 주문 (주문번호),
  FOREIGN KEY (도서번호) REFERENCES 도서 (도서번호)
);

CREATE TABLE 리뷰 (
  리뷰번호 INT PRIMARY KEY,
  회원번호 INT NOT NULL,
  도서번호 VARCHAR(10) NOT NULL,
  별점     INT NOT NULL,
  FOREIGN KEY (회원번호) REFERENCES 회원 (회원번호),
  FOREIGN KEY (도서번호) REFERENCES 도서 (도서번호)
);

INSERT INTO 회원 VALUES
  (1, '김도윤', 'GOLD'),
  (2, '이서준', 'SILVER'),
  (3, '박하윤', 'SILVER'),
  (4, '최지우', 'GOLD');

INSERT INTO 도서 VALUES
  ('B01', 'SQL 기초', 18000),
  ('B02', '데이터 모델링 입문', 21000),
  ('B03', '자바 프로그래밍', 26000),
  ('B04', '알고리즘 트레이닝', 24000),
  ('B05', '클라우드 아키텍처', 30000);

INSERT INTO 주문 VALUES
  (101, 1, '2026-01-05'),
  (102, 1, '2026-01-20'),
  (103, 2, '2026-02-01'),
  (104, 3, '2026-02-10');

INSERT INTO 주문상세 VALUES
  (101, 'B01', 1),
  (101, 'B02', 2),
  (102, 'B01', 1),
  (103, 'B03', 1),
  (104, 'B01', 1),
  (104, 'B04', 1);

INSERT INTO 리뷰 VALUES
  (1, 1, 'B01', 5),
  (2, 1, 'B02', 4),
  (3, 2, 'B01', 3),
  (4, 3, 'B03', 5);

-- 예제 1. INNER JOIN 결과 행 수는 왼쪽 한 건당 매칭 건수의 합이다
SELECT COUNT(*) AS 주문상세건수
FROM 주문 o
JOIN 주문상세 od ON o.주문번호 = od.주문번호
JOIN 도서 b ON od.도서번호 = b.도서번호;

-- 예제 2-1. ON 절에 필터를 두면 LEFT JOIN의 왼쪽 행이 모두 유지된다
SELECT m.이름, o.주문번호, o.주문일자
FROM 회원 m
LEFT JOIN 주문 o
  ON m.회원번호 = o.회원번호
 AND o.주문일자 >= '2026-02-01'
ORDER BY m.회원번호;

-- 예제 2-2. 같은 필터를 WHERE 절로 옮기면 NULL 비교 때문에 행이 사라진다
SELECT m.이름, o.주문번호, o.주문일자
FROM 회원 m
LEFT JOIN 주문 o
  ON m.회원번호 = o.회원번호
WHERE o.주문일자 >= '2026-02-01'
ORDER BY m.회원번호;

-- 예제 3. 셀프 조인 - 같은 책을 리뷰한 회원 쌍 찾기
SELECT r1.회원번호 AS 회원1, r2.회원번호 AS 회원2, r1.도서번호
FROM 리뷰 r1
JOIN 리뷰 r2
  ON r1.도서번호 = r2.도서번호
 AND r1.회원번호 < r2.회원번호;

-- 예제 4. FULL OUTER JOIN 대체 - 1월 판매 도서와 2월 판매 도서 비교
-- (MySQL 8은 FULL OUTER JOIN 문법이 없어 LEFT JOIN과 RIGHT JOIN을 UNION으로 합친다)
WITH 판매_일월 AS (
  SELECT DISTINCT od.도서번호
  FROM 주문 o
  JOIN 주문상세 od ON o.주문번호 = od.주문번호
  WHERE o.주문일자 BETWEEN '2026-01-01' AND '2026-01-31'
),
판매_이월 AS (
  SELECT DISTINCT od.도서번호
  FROM 주문 o
  JOIN 주문상세 od ON o.주문번호 = od.주문번호
  WHERE o.주문일자 BETWEEN '2026-02-01' AND '2026-02-28'
)
SELECT COALESCE(j.도서번호, f.도서번호) AS 도서번호,
       CASE WHEN j.도서번호 IS NOT NULL THEN 'Y' ELSE 'N' END AS 일월판매,
       CASE WHEN f.도서번호 IS NOT NULL THEN 'Y' ELSE 'N' END AS 이월판매
FROM 판매_일월 j
LEFT JOIN 판매_이월 f ON j.도서번호 = f.도서번호
UNION
SELECT COALESCE(j.도서번호, f.도서번호),
       CASE WHEN j.도서번호 IS NOT NULL THEN 'Y' ELSE 'N' END,
       CASE WHEN f.도서번호 IS NOT NULL THEN 'Y' ELSE 'N' END
FROM 판매_일월 j
RIGHT JOIN 판매_이월 f ON j.도서번호 = f.도서번호
ORDER BY 도서번호;

-- 예제 5. 조인 조건을 빠뜨렸을 때의 카티션 곱 (일부러 만든 실수 예시)
SELECT COUNT(*) AS 잘못된행수
FROM 주문 o, 주문상세 od;

줄별 해설

  • 예제 1: 주문과 주문상세를 조인번호로, 주문상세와 도서를 도서번호로 묶는 3중 INNER JOIN이다. 왼쪽 테이블인 주문의 각 행마다 매칭되는 주문상세 건수(2, 1, 1, 2)를 더한 값과 결과가 일치해야 정상이다.
  • 예제 2-1: LEFT JOIN의 ON 절에 o.주문일자 >= '2026-02-01'을 넣었다. 이 조건은 "매칭 대상을 고를 때"만 적용되므로, 조건에 맞는 주문이 없는 회원도 NULL이 채워진 채로 결과에 남는다.
  • 예제 2-2: 같은 조건을 WHERE로 옮겼다. LEFT JOIN이 만들어 낸 NULL 행에 대해 NULL >= '2026-02-01'을 평가하면 UNKNOWN이 되어 그 행은 최종 결과에서 제외된다.
  • 예제 3: 리뷰 테이블을 r1, r2 두 별칭으로 나눠 같은 도서번호를 가진 행끼리 묶되, r1.회원번호 < r2.회원번호로 자기 자신과의 짝, 그리고 방향이 뒤집힌 중복 쌍을 제거했다.
  • 예제 4: 1월 판매 도서 집합과 2월 판매 도서 집합을 CTE로 만든 뒤, LEFT JOIN 결과와 RIGHT JOIN 결과를 UNION으로 합쳐 FULL OUTER JOIN을 흉내 냈다. UNION은 기본적으로 중복을 제거하므로 양쪽에 모두 나오는 B01은 한 번만 남는다.
  • 예제 5: FROM 절에 주문과 주문상세를 콤마로만 나열하고 조인 조건을 주지 않았다. 문법적으로는 유효한 쿼리이지만, 두 테이블의 모든 조합인 4 × 6 = 24행을 만들어 내는 카티션 곱이다.

실행 결과

-- 예제 1 실행 결과
주문상세건수
------------
6

-- 예제 2-1 실행 결과 (ON 절에 조건, 4행)
이름   | 주문번호 | 주문일자
-------+----------+-----------
김도윤 | NULL     | NULL
이서준 | 103      | 2026-02-01
박하윤 | 104      | 2026-02-10
최지우 | NULL     | NULL

-- 예제 2-2 실행 결과 (WHERE 절에 조건, 2행)
이름   | 주문번호 | 주문일자
-------+----------+-----------
이서준 | 103      | 2026-02-01
박하윤 | 104      | 2026-02-10

-- 예제 3 실행 결과
회원1 | 회원2 | 도서번호
------+-------+---------
1     | 2     | B01

-- 예제 4 실행 결과
도서번호 | 일월판매 | 이월판매
---------+----------+---------
B01      | Y        | Y
B02      | Y        | N
B03      | N        | Y
B04      | N        | Y

-- 예제 5 실행 결과
잘못된행수
-----------
24

실무에서 자주 틀리는 것

LEFT JOIN인데 WHERE에서 NULL과 비교해 결과가 줄어듦

가장 흔한 실수다. LEFT JOIN을 쓴 이유는 오른쪽에 매칭이 없어도 왼쪽 행을 남기기 위해서인데, 그 필터 조건을 WHERE에 두면 매칭이 없어 NULL이 채워진 행까지 걸러진다.

-- 잘못된 코드: WHERE에서 필터링, 2월 주문 없는 회원이 사라짐
SELECT m.이름, o.주문번호
FROM 회원 m
LEFT JOIN 주문 o ON m.회원번호 = o.회원번호
WHERE o.주문일자 >= '2026-02-01';

-- 고친 코드: ON 절에서 필터링, 회원 4명이 모두 남음
SELECT m.이름, o.주문번호
FROM 회원 m
LEFT JOIN 주문 o
  ON m.회원번호 = o.회원번호
 AND o.주문일자 >= '2026-02-01';

조인 조건 없이 콤마로 테이블을 나열해 카티션 곱 발생

FROM 절에 테이블을 콤마로 나열만 하고 조인 조건을 빠뜨리면 두 테이블의 모든 조합이 만들어진다. 오류 없이 실행되기 때문에 행 수를 직접 세어보지 않으면 알아채기 어렵다.

-- 잘못된 코드: 조인 조건 누락, 4 x 6 = 24행
SELECT *
FROM 주문 o, 주문상세 od;

-- 고친 코드: 명시적 JOIN과 ON 조건, 6행
SELECT *
FROM 주문 o
JOIN 주문상세 od ON o.주문번호 = od.주문번호;

셀프 조인에서 <>를 써서 같은 쌍이 두 번 나옴

자기 자신과의 짝만 제거하려고 <>를 쓰면, 순서만 바뀐 두 행이 각각 조건을 만족해 같은 쌍이 두 번 나온다.

-- 잘못된 코드: (1,2)와 (2,1)이 모두 나와 2행
SELECT r1.회원번호 AS 회원1, r2.회원번호 AS 회원2
FROM 리뷰 r1
JOIN 리뷰 r2
  ON r1.도서번호 = r2.도서번호
 AND r1.회원번호 <> r2.회원번호;

-- 고친 코드: 부등호로 한 방향만 남겨 1행
SELECT r1.회원번호 AS 회원1, r2.회원번호 AS 회원2
FROM 리뷰 r1
JOIN 리뷰 r2
  ON r1.도서번호 = r2.도서번호
 AND r1.회원번호 < r2.회원번호;

조인 행 수를 예측하지 않고 SELECT *부터 실행함

여러 테이블을 한꺼번에 조인하는 쿼리를 작성할 때, 결과 행 수를 먼저 어림잡지 않고 바로 전체 컬럼을 조회하면 예상보다 훨씬 많은 행이 나왔을 때 원인 파악이 늦어진다. COUNT(*)로 행 수를 먼저 확인하는 습관을 들이면 카티션 곱이나 잘못된 조인 조건을 실행 초기에 발견할 수 있다.

-- 권장하는 순서: 먼저 행 수만 확인
SELECT COUNT(*) AS 예상행수
FROM 주문 o
JOIN 주문상세 od ON o.주문번호 = od.주문번호;

-- 행 수가 예상과 같을 때만 전체 컬럼 조회
SELECT *
FROM 주문 o
JOIN 주문상세 od ON o.주문번호 = od.주문번호;

한눈에 보기

상황별로 어떤 조인을 쓰고 행 수를 어떻게 예측할지 정리한 표
상황추천 조인결과 행 수 예측 방법흔한 실수
두 테이블의 매칭되는 행만 필요INNER JOIN왼쪽 각 행의 매칭 건수를 더한다매칭 안 되는 행이 자동으로 빠지는 것을 잊음
왼쪽 테이블 전체를 보존해야 함LEFT JOIN왼쪽 행 수 이상, 매칭 없으면 NULL 1행 포함필터 조건을 WHERE에 둬서 LEFT의 의미를 지움
양쪽 모두에서 빠진 데이터를 찾아야 함FULL OUTER(또는 UNION 대체)양쪽 고유 키의 합집합 크기MySQL에서 FULL OUTER 문법을 그대로 쓰다 오류
같은 테이블 안에서 짝을 찾아야 함셀프 조인(별칭 2개)부등호(<) 조건으로 중복·자기짝 제거 후 남는 조합 수<>를 써서 같은 쌍이 두 번 나옴

연습 문제

  1. 도서 테이블(5권)과 리뷰 테이블(4건, B01에 2건·B02에 1건·B03에 1건)을 도서번호로 조인한다. (1) 도서 LEFT JOIN 리뷰와 (2) 도서 INNER JOIN 리뷰의 결과 행 수를 각각 계산하라.
  2. 회원 테이블에서 등급이 같은 서로 다른 회원 쌍을 셀프 조인으로 구하는 쿼리를 작성하고, 예상 결과 행 수를 계산하라. (GOLD 등급: 회원 1, 4 / SILVER 등급: 회원 2, 3)
  3. 다음 쿼리에서 WHERE r.별점 >= 4 조건을 ON 절로 옮기면 결과 행 수와 내용이 어떻게 달라지는지 설명하라.
    SELECT m.이름, r.도서번호, r.별점
    FROM 회원 m
    LEFT JOIN 리뷰 r ON m.회원번호 = r.회원번호
    WHERE r.별점 >= 4;
  4. 다음 코드는 회원과 리뷰를 콤마로만 나열해 카티션 곱이 발생한다. 문제를 고치고, 잘못된 코드와 고친 코드 각각의 결과 행 수를 계산하라.
    SELECT COUNT(*)
    FROM 회원 m, 리뷰 r;

정답과 해설

  1. LEFT JOIN은 매칭 건수(2+1+1)에 매칭 없는 도서 2권(B04, B05)의 NULL 행을 더해 6행이다. INNER JOIN은 매칭 없는 도서가 빠지므로 2+1+1인 4행이다. 매칭 없는 도서 수만큼 LEFT JOIN이 INNER JOIN보다 행이 많다는 것을 확인할 수 있다.
  2. SELECT m1.회원번호 AS 회원1, m2.회원번호 AS 회원2 FROM 회원 m1 JOIN 회원 m2 ON m1.등급 = m2.등급 AND m1.회원번호 < m2.회원번호; GOLD 등급에서 (1,4) 쌍 1개, SILVER 등급에서 (2,3) 쌍 1개가 나와 총 2행이다. 등급별 회원 수가 n명이면 그 등급에서 나오는 쌍의 수는 항상 n명 중 2명을 고르는 조합 수와 같다.
  3. 조건 없이 회원과 리뷰를 LEFT JOIN 하면 회원1(2건), 회원2(1건), 회원3(1건), 회원4(NULL 1행)로 총 5행이다. 조건을 ON으로 옮기면 회원1은 별점 4 이상인 리뷰 2건이 그대로 매칭되고, 회원2는 별점 3인 리뷰가 조건을 만족하지 못해 NULL 1행으로 대체되며, 회원3은 별점 5인 리뷰 1건이 매칭되고, 회원4는 원래도 NULL 1행이라 행 수(5행)는 같지만 회원2의 실제 리뷰 데이터 대신 NULL이 보인다는 점이 달라진다. 반대로 조건을 WHERE에 두면 회원2의 행(별점 3이라 조건 불만족)과 회원4의 NULL 행(NULL >= 4는 UNKNOWN)이 모두 사라져 3행만 남고, 두 회원이 결과에서 완전히 빠진다.
  4. 잘못된 코드는 회원 4명과 리뷰 4건의 모든 조합인 4 × 4 = 16행을 반환하는 카티션 곱이다. 회원번호로 조인 조건을 추가해 SELECT COUNT(*) FROM 회원 m JOIN 리뷰 r ON m.회원번호 = r.회원번호;로 고치면, 회원1(2건)+회원2(1건)+회원3(1건)+회원4(0건)를 더한 4행이 된다.

댓글 0

아직 댓글이 없습니다. 첫 댓글을 남겨 보세요.

댓글을 남기려면 로그인이 필요합니다.