쪽지에 적힌 값들과 “같은 것이 있나”만 묻는 것이 아니라 “더 큰가, 더 작은가”를 묻고 싶을 때가 있다. 그때는 “쪽지의 값 가운데 어느 하나보다만 크면 된다”인지 “쪽지의 값 모두보다 커야 한다”인지를 함께 말해야 한다. 앞의 것이 ANY, 뒤의 것이 ALL이다. 쪽지가 비어 있으면 “모두보다 크다”는 누구에게나 참이 되고, 쪽지에 모르는 값이 하나라도 섞여 있으면 “모두보다 크다”는 아무에게도 참이 되지 않는다.
예시로 보기
IN은 쪽지의 값과 같은지만 본다. “5번 부서의 어느 직원보다도 급여가 많은 직원”처럼 크기를 견주는 질문에는 쓸 수 없다. 이 질문은 이렇게 쓴다.
SELECT Lname, Fname
FROM EMPLOYEE
WHERE Salary > ALL ( SELECT Salary
FROM EMPLOYEE
WHERE Dno = 5 );
안쪽 질의가 만든 쪽지에는 5번 부서 네 명의 급여 30000, 40000, 38000, 25000이 적혀 있다. 직원마다 자기 급여가 네 값 모두보다 큰지 본다. ALL을 ANY로 바꾸면 네 값 가운데 하나보다만 크면 된다1.
| 직원 | 급여 | > ANY (하나보다만 크면) |
> ALL (모두보다 커야) |
|---|---|---|---|
| Borg | 55000 | 참 | 참 |
| Wallace | 43000 | 참 | 참 |
| Wong | 40000 | 참 | 거짓 (40000과 같다) |
| Narayan | 38000 | 참 | 거짓 |
| Smith | 30000 | 참 | 거짓 |
| Zelaya, English, Jabbar | 25000 | 거짓 (25000보다 크지 않다) | 거짓 |
> ALL의 결과는 Wallace와 Borg 두 명이고, > ANY의 결과는 다섯 명이다.
= ANY로 쓴 Query 4(1, 2), = ALL·<> ALL·<> ANY, 빈 집합과 NULL이 섞인 집합, 카드 C2. ANY와 ALL은 SQLite에 없어 PostgreSQL 16에서 실행했다 — 61_sql-any-all_verify.sql. SQLite에서는 MAX·MIN·IN으로 바꿔 쓴 판을 돌렸다 — 61_sql-any-all_verify.py정의
비교 연산자 =, >, >=, <, <=, <> 뒤에 ANY나 ALL을 붙여, 값 v 하나를 값의 모음 V(안쪽 질의의 결과)와 견준다.
v > ANY V:V의 값 가운데 적어도 하나보다v가 크면 참이다.v > ALL V:V의 모든 값보다v가 크면 참이다.>자리에 다른 다섯 연산자를 넣어도 같은 식으로 읽는다.
기호로 쓰면, 비교 연산자를 \(\theta\)(세타, 여섯 연산자 가운데 하나)라 할 때 다음과 같다. \(\exists\)는 “~가 있다”, \(\forall\)은 “모든 ~에 대해”로 읽는다(술어와 한정기호).
\[v \;\theta\; \text{ANY}\; V \iff \exists x \in V\ (v \;\theta\; x), \qquad v \;\theta\; \text{ALL}\; V \iff \forall x \in V\ (v \;\theta\; x)\]자주 쓰는 꼴은 넷이다.
| 식 | 뜻 | 같은 말 |
|---|---|---|
v = ANY V |
V에 v와 같은 값이 있다 |
v IN V |
v <> ALL V |
V의 어느 값과도 다르다 |
v NOT IN V |
v > ALL V |
V의 가장 큰 값보다도 크다 |
대개 v > (SELECT MAX …) |
v > ANY V |
V의 가장 작은 값보다는 크다 |
대개 v > (SELECT MIN …) |
= ANY가 IN과 같으므로, 중첩 질의와 IN의 첫 질의에서 IN을 = ANY로 바꿔도 결과는 1, 2다. <> ANY는 NOT IN이 아니다. “다른 값이 하나라도 있다”는 뜻이라, 부서가 셋인 이 상태에서 Dno <> ANY ( SELECT Dnumber FROM DEPARTMENT )는 직원 8명 모두에게 참이다. = ALL은 V의 값이 모두 v와 같아야 참이라, 1번 부서(Borg 한 명)의 급여와 견주면 Borg만 남고 5번 부서와 견주면 아무도 남지 않는다1.
MAX, MIN으로 바꿔 쓸 때 달라지는 곳
> ALL V를 > (SELECT MAX …)로 바꿔 쓰는 일이 흔하다. SQLite처럼 ANY와 ALL이 없는 제품에서는 그렇게 써야 한다. 두 경우에는 결과가 달라진다2.
쪽지 V |
> ALL V |
> (SELECT MAX …) |
|---|---|---|
| 5번 부서의 급여 (네 값) | Wallace, Borg | Wallace, Borg |
| 9번 부서의 급여 (그런 부서가 없어 비어 있다) | 8명 모두 | 0명 |
프로젝트 20의 Hours (10.0, 15.0, NULL) |
0행 | 7행 |
- 빈 쪽지. “모든 값보다 크다”에서 견줄 값이 하나도 없으면 어긴 값도 없으니 참이다.
MAX는 값이 없으면 NULL을 돌려주고, NULL과의 비교는 참이 되지 않는다. - NULL이 섞인 쪽지. NULL은 모르는 값이다. 그 값보다 큰지 알 수 없으니 “모두보다 크다”는 참이 되지 못한다.
MAX는 NULL을 빼고 15.0을 돌려줘서, 15시간보다 많이 일하는 7행이 나온다(NULL 값과 IS NULL).
연결
- 선수: 중첩 질의와 IN
- “하나라도 있다”와 “모두 그렇다”를 묻는 다른 방법: EXISTS와 NOT EXISTS
ANY는 “있다”,ALL은 “모든”이다: 술어와 한정기호- 연습: 복잡한 검색 연습의 Q3-1은 가장 많이 받는 직원을
MAX로 찾는다. 같은 직원을Salary >= ALL ( … )로도 찾는다(카드 C2).
확인 문제
C1 v = ANY V와 v > ALL V의 뜻을 쓰고, 둘 가운데 IN과 같은 것을 고르라.
답: v = ANY V는 V에 v와 같은 값이 적어도 하나 있으면 참이다. v > ALL V는 v가 V의 모든 값보다 크면 참이다. = ANY가 IN과 같다.
C2 직원 급여가 Smith 30000, Wong 40000, Zelaya 25000, Wallace 43000, Narayan 38000, English 25000, Jabbar 25000, Borg 55000이고 4번 부서 직원은 Zelaya, Wallace, Jabbar다. (a) Salary >= ALL ( SELECT Salary FROM EMPLOYEE ) (b) Salary < ANY ( SELECT Salary FROM EMPLOYEE WHERE Dno = 4 )를 만족하는 직원을 각각 쓰라.
답: (a) Borg. (b) Smith, Wong, Zelaya, Narayan, English, Jabbar.
이유: (a)는 모든 직원의 급여 이상이어야 하니 가장 많이 받는 한 명이다. 자기 자신과도 견주므로 >가 아니라 >=여야 한다. (b)는 4번 부서의 급여 25000, 43000, 25000 가운데 하나보다만 작으면 되므로 43000보다 적게 받는 여섯 명이다1.
C3 Salary > ALL ( 안쪽 질의 )를 Salary > ( 같은 안쪽 질의에 MAX를 씌운 것 )으로 바꾸면 결과가 달라지는 경우를 둘 들어라.
답: ① 안쪽 질의의 결과가 비어 있을 때. > ALL은 모든 행에서 참(8명)이고, MAX는 NULL이라 0명이다. ② 안쪽 결과에 NULL이 섞여 있을 때. 프로젝트 20의 Hours(10.0, 15.0, NULL)와 견주면 > ALL은 0행, MAX는 15.0이라 7행이다.
이유: ALL은 견줄 값이 없으면 어긴 것도 없어 참이고, 모르는 값이 하나라도 있으면 참이라고 말하지 못한다. MAX는 NULL을 빼고 계산하며 값이 하나도 없으면 NULL을 돌려준다.