조인은 옆으로 붙이는 연산이라고 배웁니다. 그런데 실제로 가장 많이 바뀌는 것은 옆이 아니라 아래입니다. 행이 몇 개가 되는지를 세지 못하면 답이 갈립니다.
풀고 시작
문제 1. 주문 10건이 있고 각 주문에 상세가 3건씩 있을 때 주문과 주문상세를 조인하면 결과 행 수는?
부모의 각 행이 자식 행 수만큼 반복되므로 서른 행입니다. 이 반복이 집계에서 금액을 부풀리는 원인이 됩니다.
행수는 대응 관계가 정합니다
조인 결과의 행수를 묻는 문항은 손으로 세어 보면 반드시 풀립니다. 문제는 무엇을 세야 하는지 순서를 모르면 시간이 오래 걸린다는 점입니다. 순서를 정해 두시면 됩니다.
첫째, 어느 쪽이 일이고 어느 쪽이 다인지 정합니다. 부서와 사원이라면 부서가 일, 사원이 다입니다. 일대다 내부 조인의 결과 행수는 다 쪽에서 짝을 찾은 행의 수입니다. 부서가 다섯 개든 백 개든 상관없습니다.
둘째, 짝을 찾지 못하는 행이 있는지 봅니다. 사원의 부서코드가 NULL이거나 존재하지 않는 부서를 가리키면 그 사원은 내부 조인에서 빠집니다. 사원이 한 명도 없는 부서 역시 결과에 나타나지 않습니다.
셋째, 외부 조인이면 기준 쪽의 짝 없는 행을 더합니다. 사원을 기준으로 하면 짝 없는 사원이 남고, 부서를 기준으로 하면 사원이 없는 부서가 남습니다. 양쪽 모두를 남기는 것이 완전 외부 조인입니다.
숫자로 확인해 보겠습니다. 부서 다섯 건, 사원 스무 건, 사원 전원이 부서를 가지고 있고 두 부서에는 사원이 없다고 하겠습니다. 내부 조인은 스무 행입니다. 부서 기준 외부 조인은 스무 행에 사원 없는 부서 두 행을 더해 스물두 행입니다. 사원 기준 외부 조인은 짝 없는 사원이 없으므로 그대로 스무 행입니다.
조건이 없으면 곱해집니다
조인 조건을 빠뜨리면 모든 조합이 만들어집니다. 다섯과 스물이면 백 행입니다. 이것이 카티션 곱이고, 시험에서는 대개 조건 개수를 세게 하는 방식으로 묻습니다.
규칙은 간단합니다. 테이블이 N개면 조인 조건이 최소 N 빼기 1개 필요합니다. 세 테이블이면 두 개, 다섯 테이블이면 네 개입니다. 조건이 하나 모자라면 그만큼 곱셈이 남습니다.
여기서 한 가지 구분이 필요합니다. 실수로 만든 카티션 곱과 의도해서 만든 것은 다릅니다. 날짜 목록과 부서 목록을 곱해 데이터가 없는 구간까지 채워 보여 주려는 경우가 있습니다. 그럴 때는 CROSS JOIN이라고 문장에 적어 의도를 남깁니다. 읽는 사람이 실수인지 의도인지 구분할 수 있게 하려는 것입니다.
부푼 행 위에서 집계하면 값이 틀립니다
여기가 실무와 시험이 함께 걸리는 자리입니다. 주문과 주문상세를 조인한 뒤 주문 금액을 합계하면 어떻게 될까요. 주문 한 건의 금액이 상세 건수만큼 반복되어 있으므로 세 배로 부풀려집니다.
고치는 방법은 두 가지입니다. 행이 늘어난 뒤에 고치려 하지 말고 늘어나기 전에 줄이는 것이 정석입니다. 상세를 먼저 그룹으로 묶어 주문당 한 행으로 만든 뒤 주문과 조인하면 됩니다. 또는 조인하지 않고 스칼라 서브쿼리로 필요한 값만 가져오는 방법도 있습니다.
권하지 않는 방법도 알아 두시면 좋습니다. DISTINCT를 붙여 해결하려는 시도입니다. 금액이 우연히 같은 서로 다른 주문까지 하나로 지워 버리기 때문에 조용히 틀린 답이 나옵니다. 오류가 나지 않아서 더 위험합니다.
집계 대상이 부모 쪽 값인지 자식 쪽 값인지 먼저 확인하는 습관을 들이시면 됩니다. 자식 쪽 값이라면 조인 후 집계해도 맞고, 부모 쪽 값이라면 부풀림을 의심해야 합니다.
그룹과 윈도우는 방향이 다릅니다
행이 줄어드는지 남는지도 자주 갈리는 자리입니다.
GROUP BY는 행을 줄입니다. 부서별로 묶으면 사원 개개인의 행은 사라지고 부서마다 한 행만 남습니다. 그래서 SELECT 절에는 그룹 기준 컬럼과 집계 함수만 올 수 있습니다. 그룹 안에서 값이 하나로 정해지지 않는 컬럼은 무엇을 보여 줄지 알 수 없기 때문입니다.
윈도우 함수는 행을 남깁니다. 부서로 나눈 윈도우에 평균을 구하면 사원마다 자기 급여와 자기 부서 평균이 나란히 나옵니다. 행수는 그대로입니다.
그룹 함수는 행을 늘립니다. ROLLUP이나 CUBE는 소계 행을 추가합니다. 부서 세 개와 직급 네 개가 모두 존재할 때 두 컬럼으로 ROLLUP하면 조합 열두 행에 부서별 소계 세 행과 전체 한 행을 더해 열여섯 행입니다. CUBE는 여기에 직급별 소계 네 행이 더 붙어 스무 행입니다.
셋을 한 줄로 정리하면 이렇습니다. 줄이는 것이 GROUP BY, 그대로 두는 것이 윈도우 함수, 늘리는 것이 그룹 함수입니다.
인출 문제
문제 1. 부서 5건과 사원 20건이 있고 사원 중 3명의 부서코드가 NULL일 때 내부 조인 결과 행 수는?
부서코드가 있는 열일곱 명만 짝을 찾습니다. NULL인 세 명은 어떤 부서와도 같지 않아 빠집니다.
문제 2. 네 개의 테이블을 조인할 때 필요한 최소 조인 조건 수는?
테이블 수보다 하나 적은 만큼이 필요합니다. 하나가 모자라면 그만큼 곱셈이 남습니다.
문제 3. 주문과 주문상세를 조인한 뒤 주문 금액을 합계하면 값이 부풀려지는 이유는?
조인은 행을 늘립니다. 부모 값을 집계하려면 자식을 먼저 묶어 한 행으로 줄여야 합니다.
문제 4. 부서 3개와 직급 4개가 모두 존재할 때 두 컬럼으로 CUBE를 적용하면 결과 행 수는?
조합 열둘에 부서별 셋과 직급별 넷과 전체 하나를 더해 스물입니다. ROLLUP이면 열여섯입니다.
더 풀기
출제기준의 세세항목을 따라 이 강의 범위에서 새로 낸 문제입니다. 모의고사도 여기에서 뽑습니다.
묶음 1
문제 1. 행이 각각 8건과 5건인 두 테이블을 조인 조건 없이 조회하면 결과 행 수는?
모든 조합이 만들어지므로 여덟 곱하기 다섯인 마흔 행입니다.
문제 2. 부서 4건과 사원 12건이고 모든 사원이 부서를 하나씩 가질 때 내부 조인 결과 행 수는?
사원 한 명마다 부서 하나가 대응하므로 사원 수 그대로입니다.
문제 3. 같은 상황에서 한 부서에는 사원이 없다면 부서 기준 외부 조인 결과 행 수는?
짝지어진 열두 행에 사원이 없는 부서 한 행을 더합니다.
문제 4. 왼쪽 12건, 오른쪽 9건이고 그중 7건이 서로 짝지어질 때 완전 외부 조인 결과 행 수는?
짝지은 일곱에 왼쪽만 있는 다섯과 오른쪽만 있는 둘을 더해 열넷입니다.
문제 5. 같은 상황에서 왼쪽 기준 외부 조인 결과 행 수는?
왼쪽의 열두 건이 모두 남습니다.
문제 6. 같은 상황에서 내부 조인 결과 행 수는?
양쪽에서 조건이 참인 일곱 행만 남습니다.
문제 7. 왼쪽 외부 조인의 결과 행 수가 왼쪽 테이블 행 수보다 많아질 수 있나요?
외부 조인은 기준 쪽 행 수를 최소로 보장할 뿐 상한을 정하지 않습니다.
문제 8. 부서 3건이고 각 부서의 사원이 각각 2명, 0명, 5명일 때 부서 기준 왼쪽 외부 조인 결과 행 수는?
두 행과 다섯 행에 사원이 없는 부서 한 행을 더해 여덟입니다.
문제 9. 사원 10명 중 관리자가 없는 사원이 1명일 때 사원과 관리자를 내부 셀프 조인하면 결과 행 수는?
관리자가 없는 한 명은 짝을 찾지 못해 빠집니다. 외부 조인으로 바꾸면 열 행입니다.
문제 10. 다섯 개의 테이블을 조인할 때 조인 조건을 세 개만 썼다면 어떤 일이 벌어지나요?
최소 네 개가 필요합니다. 하나가 빠지면 두 집합이 조건 없이 곱해집니다.
묶음 2
문제 1. 일대다 조인 결과에서 부모 금액을 정확히 합계하는 방법으로 가장 알맞은 것은?
늘어나기 전에 줄이는 것이 정석입니다. DISTINCT는 금액이 같은 서로 다른 주문까지 지워 버립니다.
문제 2. DISTINCT로 부풀림을 해결하려는 시도가 위험한 이유로 가장 알맞은 것은?
오류가 나지 않아 더 위험합니다. 검산하지 않으면 발견되지 않습니다.
문제 3. 조인 대신 스칼라 서브쿼리로 값을 가져올 때의 장점으로 가장 알맞은 것은?
다만 행마다 실행되므로 대상이 많으면 비용이 커질 수 있습니다.
문제 4. 존재 여부만 확인하면 되는데 조인을 쓰면 생기는 문제는?
EXISTS는 몇 건이 있든 한 번만 참입니다. 존재 확인에는 EXISTS가 적합합니다.
문제 5. 조인 결과에 중복 행이 예상보다 많다면 가장 먼저 확인할 것은?
행이 부푸는 원인은 대개 조건 누락이거나 예상과 달리 일대다인 관계입니다.
문제 6. GROUP BY와 윈도우 함수의 결과 행 수 차이를 바르게 설명한 것은?
개별 값과 전체 맥락을 함께 보려면 윈도우 함수를 씁니다.
문제 7. 부서 3개와 직급 4개가 모두 존재할 때 두 컬럼으로 ROLLUP을 적용하면 결과 행 수는?
조합 열둘에 부서별 소계 셋과 전체 하나를 더해 열여섯입니다.
문제 8. N개의 컬럼에 CUBE를 적용하면 만들어지는 집계 수준의 개수는?
각 컬럼을 포함하거나 빼는 모든 조합이므로 이의 N 제곱입니다.
문제 9. N개의 컬럼에 ROLLUP을 적용하면 만들어지는 집계 수준의 개수는?
오른쪽부터 하나씩 지워 가므로 원래 조합부터 전체까지 N 더하기 1 단계입니다.
문제 10. 부서가 3개이고 직급이 4개인데 부서와 직급으로 묶은 결과가 7행이라면 무엇을 뜻하나요?
데이터에 없는 조합은 그룹이 되지 않습니다. 모든 조합을 보려면 기준 집합을 만들어 외부 조인해야 합니다.
묶음 3
문제 1. 결과 A가 7행, 결과 B가 4행이고 겹치는 행이 3행일 때 UNION의 결과 행 수는?
합치면 열하나이지만 겹친 세 행이 하나씩 줄어 여덟입니다.
문제 2. 같은 상황에서 UNION ALL의 결과 행 수는?
중복을 제거하지 않으므로 그대로 더한 열하나입니다.
문제 3. 같은 상황에서 교집합 연산의 결과 행 수는?
양쪽에 모두 있는 세 행만 남습니다.
문제 4. 같은 상황에서 A에서 B를 뺀 차집합의 결과 행 수는?
일곱 행 중 겹친 세 행이 빠져 네 행입니다.
문제 5. 같은 상황에서 B에서 A를 뺀 차집합의 결과 행 수는?
네 행 중 겹친 세 행이 빠져 한 행입니다. 차집합은 순서에 따라 결과가 달라집니다.
문제 6. 집합 연산자와 조인의 차이를 행수 관점에서 바르게 설명한 것은?
방향이 다릅니다. 조인은 컬럼이 늘고 집합 연산은 행이 쌓입니다.
문제 7. 급여가 300, 200, 200, 100인 네 행에 급여 내림차순으로 RANK를 매기면 마지막 사원의 순위는?