SQL 전문가(SQLP) 시험 노트개념 정리19 MIN
조인 종류와 표준 조인 문법
SQLP 2과목의 셋째 자리입니다. EQUI 조인과 NON-EQUI 조인, ON 절과 USING 절, NATURAL JOIN과 CROSS JOIN, 아우터 조인 세 가지, 조인 조건과 필터 조건을 나눠 쓰는 법, 조인 조건 누락과 1:M 조인이 건수를 늘리는 자리를 다룹니다.
조인은 흩어 놓은 것을 다시 모으는 연산입니다. 1과목에서 정규화로 테이블을 갈라 놓았으니, 값을 함께 보려면 반드시 조인을 지나야 합니다. 그래서 조인 문항은 문법을 묻는 것처럼 보여도 실은 결과 집합의 건수를 묻습니다. 어느 행이 살아남고 어느 행이 몇 배로 늘어나는지를 셀 수 있으면 이 자리의 문항은 대부분 계산 문제가 됩니다.
EQUI 조인과 NON-EQUI 조인
등호로 잇는 조인
EQUI 조인은 조인 조건이 등호인 조인입니다. 우리가 쓰는 조인의 거의 전부가 여기에 들어갑니다 — 기본키와 외래키를 잇는 조건이 곧 등호이기 때문입니다.
SELECT e.사원명, d.부서명
FROM 사원 e
JOIN 부서 d ON d.부서번호 = e.부서번호;
범위로 잇는 조인
등호가 아닌 조건으로 잇는 것이 NON-EQUI 조인입니다. 두 테이블 사이에 일치하는 키가 없고 구간으로만 대응할 때 씁니다.
SELECT e.사원명, g.등급
FROM 사원 e
JOIN 급여등급 g ON e.급여 BETWEEN g.최저급여 AND g.최고급여;
급여등급 테이블이 1등급 1000~2000, 2등급 2001~3000 식으로 구간을 담고 있으면 사원의 급여가 어느 구간에 드는지로 짝을 찾습니다. 구간이 겹치게 설계되어 있으면 사원 한 명이 등급 둘에 걸려 행이 두 배가 되므로, 구간의 경계가 서로 물리지 않는지 확인하는 것이 이 조인의 첫 점검입니다.
표준 조인 문법
INNER JOIN과 ON
표준 SQL은 조인을 FROM 절에서 선언하고 조건을 ON에 답니다. INNER는 생략할 수 있어 JOIN만 적어도 내부 조인입니다. 내부 조인은 양쪽에 짝이 있는 행만 남기는 조인입니다.
ON 절에는 등호가 아닌 조건도, 여러 조건도 적을 수 있습니다. 조인 조건이 여럿이면 AND로 잇습니다.
USING 절
양쪽 칼럼 이름이 같을 때는 USING으로 줄여 쓸 수 있습니다.
SELECT 부서번호, e.사원명, d.부서명
FROM 사원 e JOIN 부서 d USING (부서번호);
여기에 시험에 자주 나오는 제약이 하나 있습니다 — USING에 적은 칼럼에는 테이블 한정자를 붙일 수 없습니다. 위 질의에서 e.부서번호라고 적으면 오류가 납니다. USING이 두 칼럼을 하나로 합쳐 버려 어느 쪽 것인지 가릴 이유가 없어졌기 때문입니다. USING 칼럼에 괄호 안 한정자를 붙이는 것(USING (e.부서번호))도 마찬가지로 오류입니다.
NATURAL JOIN과 CROSS JOIN
NATURAL JOIN은 조건을 적지 않습니다. 두 테이블에서 이름과 타입이 같은 모든 칼럼을 찾아 자동으로 등가 조인합니다.
SELECT 사원명, 부서명 FROM 사원 NATURAL JOIN 부서;
편해 보이지만 실무에서는 거의 쓰지 않습니다. 나중에 두 테이블에 수정일자 같은 이름이 함께 생기면 조인 조건이 말없이 하나 늘어나 결과가 바뀌기 때문입니다. NATURAL JOIN에는 ON이나 USING을 함께 쓸 수 없고, 조인에 쓰인 칼럼에 한정자를 붙일 수 없는 것은 USING과 같습니다.
CROSS JOIN은 반대로 조건 없이 모든 조합을 만듭니다. 4행과 10행을 CROSS JOIN하면 40행이 나옵니다. 이것이 1과목에서 본 카티션 곱이고, 여기서는 실수가 아니라 의도해서 적는 문법입니다. 날짜 목록과 부서 목록을 곱해 빈칸 없는 통계 틀을 만드는 자리에 씁니다.
아우터 조인
보존할 쪽 고르기
아우터 조인은 짝이 없는 행도 살려 두는 조인입니다. 어느 쪽을 살릴지에 따라 셋으로 나뉩니다.
| 문법 | 살아남는 행 |
|---|---|
A LEFT OUTER JOIN B |
A의 모든 행. 짝 없는 B 칼럼은 NULL |
A RIGHT OUTER JOIN B |
B의 모든 행 |
A FULL OUTER JOIN B |
양쪽 모두 |
OUTER는 생략할 수 있습니다. LEFT와 RIGHT는 테이블 순서만 바꾸면 서로 옮겨 쓸 수 있으므로, 팀 안에서 한쪽으로 통일해 두면 읽기 쉬워집니다.
오라클 전용 표기
표준 문법이 들어오기 전 오라클은 (+) 기호로 아우터 조인을 표시했습니다. 보존할 쪽이 아닌 테이블 칼럼에 (+)를 붙입니다.
-- 사원을 보존한다 — 표준 문법의 LEFT OUTER JOIN과 같다
SELECT e.사원명, d.부서명
FROM 사원 e, 부서 d
WHERE e.부서번호 = d.부서번호(+);
방향이 헷갈리기 쉬운데 「모자란 쪽에 기호를 붙여 채운다」로 외우면 됩니다. 이 표기로는 FULL OUTER JOIN을 만들 수 없습니다 — 양쪽에 (+)를 붙이면 오류입니다. 옛 코드를 읽을 일이 있어 알아 두되, 새로 쓸 때는 표준 문법을 씁니다.
ON과 WHERE의 분담
조인 조건과 필터 조건을 어느 절에 적을지가 결과를 가릅니다. 내부 조인에서는 두 절이 같은 일을 하지만, 아우터 조인에서는 다릅니다.
아우터 조인이 내부 조인으로 바뀌는 자리
-- 사용 중인 배송지가 있는 회원만 남는다 — 사실상 내부 조인
FROM 회원 m LEFT JOIN 배송지 b ON b.회원번호 = m.회원번호
WHERE b.사용여부 = 'Y'
-- 배송지가 없는 회원도 남고, 배송지는 사용 중인 것만 붙는다
FROM 회원 m LEFT JOIN 배송지 b
ON b.회원번호 = m.회원번호 AND b.사용여부 = 'Y'
ON은 짝을 맞출 때 적용되고 WHERE는 조인이 끝난 뒤에 적용됩니다. 위쪽은 배송지가 없는 회원이 일단 NULL로 채워져 남았다가 WHERE b.사용여부 = 'Y'를 통과하지 못해 그 자리에서 사라집니다. LEFT JOIN이라고 적어 두고도 결과는 내부 조인과 같아지는 것입니다.
어느 쪽 조건인가로 가른다
규칙은 한 줄입니다 — 보존할 쪽이 아닌 테이블의 조건은 ON에 둡니다. 위 예에서 보존할 쪽은 회원이고 배송지가 아닌 쪽이므로, b.사용여부 = 'Y'는 ON에 붙어야 합니다.
반대로 보존할 쪽 테이블의 조건은 WHERE에 두어도 됩니다. WHERE m.가입일 >= DATE '2026-01-01'은 회원을 거르는 조건이라 아우터 조인의 성질을 건드리지 않습니다. 짝이 없어 NULL로 채워지는 것은 배송지 쪽 칼럼이지 회원 쪽 칼럼이 아니기 때문입니다.
여러 테이블 조인
조인 조건의 최소 개수
테이블 개를 이으려면 조인 조건이 최소 개 필요합니다. 셋을 조인하는데 ON이 하나뿐이면 나머지 하나가 조건 없이 붙은 것이고, 그 순간 결과가 카티션 곱으로 부풀어 오릅니다.
SELECT o.주문번호, d.상품번호, p.상품명
FROM 주문 o
JOIN 주문상세 d ON d.주문번호 = o.주문번호
JOIN 상품 p ON p.상품번호 = d.상품번호; -- 이 줄이 빠지면 상품 전체가 곱해진다
표준 문법을 쓰면 이 사고가 잘 나지 않습니다. JOIN 뒤에 ON을 적지 않으면 문법 오류이기 때문입니다. FROM A, B, C 꼴로 적고 조건을 WHERE에 몰아넣는 옛 문법에서는 조건 하나가 빠져도 질의가 그냥 돌아갑니다. 표준 문법을 쓰는 첫째 이유가 이것입니다.
조인 차수
조인은 한 번에 두 집합씩 처리됩니다. 테이블이 다섯이면 조인 단계가 넷이고, 어떤 순서로 붙이는지에 따라 중간 결과의 크기가 달라집니다. 어느 순서로 갈지는 옵티마이저가 정하지만, 1과목에서 본 대로 먼저 많이 걸러지는 쪽부터 읽는 것이 유리하다는 원칙은 그대로입니다.
1:M 조인과 결과 건수
건수가 늘어나는 자리
1:M 관계를 조인하면 1쪽의 행이 M쪽 건수만큼 복제됩니다. 주문 3건에 주문상세가 각각 2건, 1건, 3건 달려 있으면 조인 결과는 행이고, 주문 행은 각각 그 횟수만큼 되풀이됩니다.
이것은 오류가 아니라 조인의 정의입니다. 문제는 그 결과 위에서 집계할 때 생깁니다.
집계가 부풀려지는 사고
-- 주문 금액이 상세 건수만큼 중복으로 더해진다
SELECT SUM(o.주문금액)
FROM 주문 o JOIN 주문상세 d ON d.주문번호 = o.주문번호;
주문 금액이 1000·2000·3000이고 상세가 각각 2·1·3건이라면 결과는 입니다. 실제 합계 과 전혀 다릅니다.
고치는 방향은 둘입니다. 상세가 조건에만 필요하다면 조인 대신 EXISTS로 바꿔 건수를 늘리지 않는 것, 상세의 값도 함께 써야 한다면 상세를 먼저 주문번호별로 집계한 인라인 뷰로 만들어 1:1로 붙이는 것입니다. SELECT SUM(DISTINCT ...)로 덮는 방법이 가장 흔한 오답인데, 금액이 같은 주문이 둘 있으면 그중 하나가 통째로 사라집니다.
연습 문제
부서 테이블에 10·20·30·40 네 행이 있고, 사원 10명 중 3명은 부서 10, 5명은 부서 20에 속하며 2명은 부서번호가 NULL이다. 부서 30과 40에는 사원이 없다. 사원과 부서를 부서번호로 내부 조인했을 때와 FULL OUTER JOIN 했을 때의 결과 건수는?
① 8건, 10건
② 8건, 12건
③ 10건, 12건
④ 8건, 14건②. 내부 조인은 짝이 있는 행만 남기므로 건입니다. FULL OUTER JOIN은 여기에 짝이 없는 사원 2명과 사원이 없는 부서 2개를 더하므로 건입니다. 뒤쪽 값으로 10을 고르면 사원만 보존하는 LEFT OUTER JOIN의 건수입니다.위와 같은 데이터에서
SELECT * FROM 사원 CROSS JOIN 부서의 결과 건수는?
① 14건
② 8건
③ 40건
④ 오류가 난다③.CROSS JOIN은 조건 없이 모든 조합을 만들므로 행입니다. 부서번호가 NULL인 사원도 조인 조건이 없으니 그대로 네 부서와 짝지어집니다.다음 중 오류가 나는 것은?
①SELECT 부서번호 FROM 사원 e JOIN 부서 d USING (부서번호)
②SELECT e.부서번호 FROM 사원 e JOIN 부서 d USING (부서번호)
③SELECT e.사원명, d.부서명 FROM 사원 e JOIN 부서 d USING (부서번호)
④SELECT e.사원명 FROM 사원 e JOIN 부서 d ON d.부서번호 = e.부서번호②.USING에 적은 칼럼은 두 테이블의 것이 하나로 합쳐지므로 테이블 한정자를 붙일 수 없습니다. ①처럼 한정자 없이 적어야 하고, ③의사원명·부서명은USING칼럼이 아니므로 한정자를 붙여도 됩니다.주문 세 건의 금액이 1000·2000·3000이고 주문상세가 각각 2건·1건·3건 달려 있다.
SELECT SUM(o.주문금액) FROM 주문 o JOIN 주문상세 d ON d.주문번호 = o.주문번호의 결과는?
① 6,000
② 13,000
③ 18,000
④ 36,000②. 조인 결과는 6행이고 각 주문 행이 상세 건수만큼 되풀이되므로 입니다. 실제 합계 6,000을 얻으려면 상세를 먼저 집계해 1:1로 붙이거나 조인을EXISTS로 바꿔야 합니다.회원 100명 중 20명은 배송지가 없고, 배송지가 있는 80명 중 절반은 사용여부가
'N'이다. 다음 두 질의의 결과 건수는?
㉠FROM 회원 m LEFT JOIN 배송지 b ON b.회원번호 = m.회원번호 WHERE b.사용여부 = 'Y'
㉡FROM 회원 m LEFT JOIN 배송지 b ON b.회원번호 = m.회원번호 AND b.사용여부 = 'Y'
① 40건, 100건
② 100건, 100건
③ 40건, 60건
④ 80건, 100건①. 회원마다 배송지가 하나씩이라고 보면 ㉠은 사용여부가'Y'인 40명만 남습니다. 배송지가 없는 20명은b.사용여부가 NULL이라WHERE를 통과하지 못하고,'N'인 40명도 걸러집니다. ㉡은 조건이ON에 있어 짝을 맞출 때만 쓰이므로 회원 100명이 모두 남고 배송지 칼럼만 NULL로 채워집니다.세 테이블을
FROM 주문 o, 주문상세 d, 상품 p꼴로 적고WHERE d.주문번호 = o.주문번호하나만 둔 질의가 있다. 주문 1,000건, 주문상세 4,000건, 상품 500건일 때 예상되는 결과 건수를 계산하고, 이런 사고가 표준 조인 문법에서는 잘 생기지 않는 이유를 서술하시오.주문과 주문상세는 조건으로 이어져 있어 4,000행이 만들어지지만, 상품은 어떤 조건과도 이어져 있지 않아 그 4,000행 각각에 상품 500행이 모두 곱해집니다. 따라서 행입니다. 테이블 개를 이으려면 조인 조건이 최소 개 필요한데 셋을 이으면서 하나만 두었으니 하나가 빠진 것입니다. 표준 조인 문법에서는JOIN키워드 뒤에ON이나USING을 적지 않으면 문법 오류가 나므로, 조건을 빠뜨린 채로는 질의가 아예 실행되지 않습니다. 의도한 카티션 곱은CROSS JOIN이라고 따로 적어야 하므로 의도와 실수가 문법에서 갈립니다. 반면 쉼표로 나열하고 조건을WHERE에 몰아넣는 옛 문법에서는 조건 하나가 빠져도 질의가 정상으로 돌아가고, 결과 건수가 커지기 전까지는 드러나지 않습니다. 채점은 2,000,000이라는 값과 계산 근거에 3점, 규칙을 든 것에 1점, 표준 문법이 문법 오류로 막는다는 설명에 1점입니다.

