행을 고르는 조건을 적지 않으면 모든 행이 나오고, 표가 둘이면 한 표의 행 하나하나가 다른 표의 모든 행과 짝지어져 나온다. 학생 8명과 동아리 3개를 조건 없이 늘어놓으면 “누가 어느 동아리인지”가 아니라 있을 수 있는 24가지 짝이 전부 나오는 것과 같다. 오류가 나지 않아서 더 위험하다. 조건 하나를 빠뜨리면 행 수가 몇 배로 불어난 틀린 답이 아무 경고 없이 나온다.
예시로 보기
조건은 “이런 행만”이라고 좁히는 말이다. 그 말을 안 하면 좁혀지지 않는다.
SELECT Ssn
FROM EMPLOYEE;
직원 여덟 명의 Ssn이 모두 나온다. 여기까지는 바라던 대로다. 표를 둘 적으면 이야기가 달라진다.
SELECT Ssn, Dname
FROM EMPLOYEE, DEPARTMENT;
24행이 나온다. 직원 한 명마다 부서 셋이 모두 붙는다.
| Ssn | Dname |
|---|---|
| 123456789 | Research |
| 123456789 | Administration |
| 123456789 | Headquarters |
| 333445555 | Research |
| 333445555 | Administration |
| 333445555 | Headquarters |
| … (나머지 여섯 명도 세 번씩) | … |
Smith(123456789)는 Research에서만 일한다. 그런데 결과에는 Administration, Headquarters와도 짝지어져 있다. 24행 가운데 사실인 짝은 8행뿐이고 16행은 사실이 아니다. WHERE Dno = Dnumber를 붙여야 그 8행만 남는다.
정의
WHERE 절이 없으면 튜플을 고르는 조건이 없다. FROM에 적힌 릴레이션의 모든 튜플이 나온다. 릴레이션이 둘 이상이면 그 릴레이션들의 튜플로 만들 수 있는 모든 조합이 나온다.
이렇게 만든 “모든 조합”을 두 표의 카티션 곱(cross product)이라 부른다1.
기호로 쓰면. 표 \(R\)과 \(S\)의 카티션 곱은 \(R\)의 행 하나와 \(S\)의 행 하나를 붙인 짝을 모두 모은 것이다.
\[R \times S = \{\, (r, s) \;:\; r \in R,\ s \in S \,\}, \qquad \vert R \times S\vert = \vert R\vert \cdot \vert S\vert\]\(r\)은 \(R\)의 행 하나, \(s\)는 \(S\)의 행 하나다. \(\vert R\vert\)은 \(R\)의 행 수다. 행 수는 두 표의 행 수를 곱한 것이다. EMPLOYEE가 8행, DEPARTMENT가 3행이라 8 × 3 = 24행이 나왔다. 집합의 곱집합과 같은 식이다.
그래서 선택 조건과 조인 조건을 WHERE 절에 하나도 빠짐없이 적는 것이 아주 중요하다. 하나라도 빠뜨리면 틀리고 아주 큰 릴레이션이 결과로 나올 수 있다.
예제
조건을 하나씩 빠뜨리면 결과가 얼마나 불어나는지 본다1. 질의는 SELECT-FROM-WHERE의 Q1과 Q2다.
| 질의 | 조건을 다 적으면 | 조인 조건 하나를 빠뜨리면 | WHERE를 통째로 빠뜨리면 |
|---|---|---|---|
| Q1: Research 직원 (표 둘) | 4행 | 8행 | 24행 |
| Q2: Stafford의 프로젝트와 관리자 (표 셋) | 2행 | 16행 (Mgr_ssn = Ssn을 빠뜨림) 또는 6행 (Dnum = Dnumber를 빠뜨림) |
144행 |
Q1에서 조인 조건을 빠뜨린 8행은 “직원 여덟 명 모두”다. 다른 부서 사람까지 Research 직원으로 나온다. 행 수가 4에서 8로 겨우 두 배라 눈에 잘 안 띈다.
표가 많아지면 곱은 금세 커진다. COMPANY의 여섯 표를 조건 없이 한 FROM에 적으면 8 × 3 × 5 × 6 × 16 × 7 = 80,640행이 나온다. 행이 몇십 개뿐인 연습용 표에서 이 정도다.
한쪽 표가 비어 있으면 조합도 0개다. 곱하는 수 가운데 0이 있으면 곱이 0인 것과 같다.
활용
- 빠뜨린 것을 알아채는 법. 결과의 행 수를 어림한다. “직원이 8명인데 왜 24행이지?” 싶으면 조건이 빠졌다. 또
FROM의 표 수를 센다. 표가 \(n\)개인데 표끼리 잇는 조건이 \(n - 1\)개보다 적으면 어딘가 이어지지 않은 표가 있다1. - 조인 조건을
FROM에 적기. 조인 조건을WHERE에 섞어 쓰면 빠뜨려도 티가 안 난다.FROM에서 표를 이을 때 조건을 바로 붙여 쓰는 방법이 있다: FROM 절의 조인 (JOIN ON). - 행이 나오는 차례. 결과의 행이 어떤 차례로 나오는지는 정해져 있지 않다. SQLite에서
SELECT Ssn FROM EMPLOYEE;를 돌리면 행을 넣은 차례(Smith, Wong, Zelaya, …)가 아니라 번호가 작은 것부터 나온다. 차례를 정하려면 따로 적어야 한다: ORDER BY와 질의의 절 순서1.
연결
- 선수: SELECT-FROM-WHERE
- 같은 식: 집합의 곱집합 \(A \times B\)와 \(\vert A \times B\vert = \vert A\vert \,\vert B\vert\)
- 조합을 걸러 내는 방법들: 내부 조인과 외부 조인 비교
- 한 표를 자기 자신과 곱하기: 별칭과 자기 조인
확인 문제
C1 SELECT Pname, Dname FROM PROJECT, DEPARTMENT;는 몇 행을 내는가? 그 가운데 “프로젝트와 그것을 맡은 부서”라는 사실에 맞는 짝은 몇 행인가? 맞는 짝만 남기려면 무엇을 붙이는가?
답: 6 × 3 = 18행이다. 맞는 짝은 6행이다. WHERE Dnum = Dnumber를 붙인다.
이유: 프로젝트 여섯 개가 각각 부서 셋과 모두 짝지어진다. 프로젝트 하나를 맡은 부서는 하나뿐이므로 맞는 짝은 프로젝트 수와 같은 6개다.
C2 조인 조건을 빠뜨린 질의가 왜 오류가 아닌가? 그래서 왜 더 위험한가?
답: WHERE 절은 없어도 되는 절이고, “모든 조합을 달라”도 문법에 맞는 질의이기 때문이다. DBMS는 사용자가 어떤 짝을 바랐는지 모른다. 오류가 안 나므로 틀린 답을 맞는 답으로 믿고 쓰게 된다.
이유: Q1에서 조인 조건을 빠뜨리면 4행이어야 할 답이 8행으로 나온다. 행 수가 조금만 달라 눈으로는 알아채기 어렵다.
C3 “직원과 그 직원이 일하는 프로젝트의 이름”을 구하려고 FROM EMPLOYEE, WORKS_ON, PROJECT를 적었다. 조인 조건 둘(Ssn = Essn, Pno = Pnumber)을 다 적으면 16행이다. 하나씩 빠뜨리면 각각 몇 행이 되는가? (EMPLOYEE 8행, WORKS_ON 16행, PROJECT 6행)
답: Pno = Pnumber를 빠뜨리면 16 × 6 = 96행, Ssn = Essn을 빠뜨리면 8 × 16 = 128행이다.
이유: 남은 조건이 이어 준 두 표의 결과는 16행이다. 이어지지 않은 셋째 표의 모든 행이 그 16행 하나하나에 붙는다.