subquery란 SELECT, INSERT, UPDATE, DELETE에 포함된 query를 말한다.
outer query : subquery를 포함하는 query를 말한다.
subquery는 ()안에 기술된다.
| id가 14인 임직원보다 생일이 빠른 임직원의 id,이름,생일을 출력하라
mysql> SELECT birth_date FROM employee WHERE id = 14;
+------------+
| birth_date |
+------------+
| 1992-08-04 |
+------------+
1 row in set (0.04 sec)
mysql> SELECT id, name, birth_date FROM employee WHERE birth_date < '1992-08-04';
+----+--------+------------+
| id | name | birth_date |
+----+--------+------------+
| 1 | MESSI | 1987-02-01 |
| 5 | DINGYO | 1990-11-05 |
| 6 | JULIA | 1986-12-11 |
| 9 | HENRY | 1982-05-20 |
| 10 | NICOLE | 1991-03-26 |
| 13 | JISUNG | 1989-07-07 |
+----+--------+------------+
mysql> SELECT id, name, birth_date FROM employee
-> WHERE birth_date < (SELECT birth_date FROM employee WHERE id = 14);
+----+--------+------------+
| id | name | birth_date |
+----+--------+------------+
| 1 | MESSI | 1987-02-01 |
| 5 | DINGYO | 1990-11-05 |
| 6 | JULIA | 1986-12-11 |
| 9 | HENRY | 1982-05-20 |
| 10 | NICOLE | 1991-03-26 |
| 13 | JISUNG | 1989-07-07 |
+----+--------+------------+
| id가 1인 임직원과 같은 부서, 같은 성별인 임직원들의 id,이름,직군을 출력하라
mysql> SELECT id, name, position FROM employee
-> WHERE (dept_id, sex) = (
-> SELECT dept_id, sex
-> FROM employee
-> WHERE id = 1
-> );
+----+-------+-----------+
| id | name | position |
+----+-------+-----------+
| 1 | MESSI | DEV_BACK |
| 14 | SAM | DEV_INFRA |
+----+-------+-----------+
v IN (v1, v2, v3 ...)
: v가 (v1, v2, v3 ...) 중 하나라도 값이 같다면 true를 리턴한다.
(v1, v2, v3 ...) 안에 subquery의 결과가 들어갈 수 있다.
v NOT IN (v1, v2, v3 ...)
: v가 (v1, v2, v3 ...) 의 모든 값과 일치하지 않을 때 true를 리턴한다.
id가 5인 임직원과 같은 프로젝트에 참여한 임직원들의 id를 출력하라
mysql> SELECT DISTINCT empl_id FROM works_on
-> WHERE empl_id <> 5 AND proj_id IN (
-> SELECT proj_id FROM works_on WHERE empl_id = 5);
+---------+
| empl_id |
+---------+
| 1 |
| 3 |
| 10 |
| 13 |
| 14 |
+---------+
| id가 5인 임직원과 같은 프로젝트에 참여한 임직원들의 id, 이름을 출력하라
이름은 works_on 테이블만으로는 알 수 없다. employee 테이블에서 위에서 구한 id와 대응하는 이름들을 조회해야한다.
mysql> SELECT id, name
-> FROM employee WHERE id IN (
-> SELECT DISTINCT empl_id FROM works_on
-> WHERE empl_id <> 5 AND proj_id IN (
-> SELECT proj_id FROM works_on WHERE empl_id = 5));
+----+--------+
| id | name |
+----+--------+
| 1 | MESSI |
| 3 | JENNY |
| 10 | NICOLE |
| 13 | JISUNG |
| 14 | SAM |
+----+--------+
같은 조건을 반환하지만 subquery를 FROM 절에서도 쓸 수 있다.
mysql> SELECT id, name
-> FROM employee, (
-> SELECT DISTINCT empl_id FROM works_on
-> WHERE empl_id <> 5 AND proj_id IN (
-> SELECT proj_id FROM works_on WHERE empl_id = 5))
-> AS E
-> WHERE id = E.empl_id;
+----+--------+
| id | name |
+----+--------+
| 1 | MESSI |
| 3 | JENNY |
| 10 | NICOLE |
| 13 | JISUNG |
| 14 | SAM |
+----+--------+
EXISTS
: subquery의 결과가 최소 하나의 row라도 있다면 true 반환
NOT EXISTS
: subquery의 결과가 단 하나의 row도 없다면 true 반환
| id가 7 혹은 12인 임직원이 참여한 프로젝트의 id, 이름을 출력하라
mysql> SELECT P.id, P.name
-> FROM project P
-> WHERE EXISTS (
-> SELECT *
-> FROM works_on W
-> WHERE W.proj_id = P.id AND W.empl_id IN (7, 12)
-> );
+------+------------------+
| id | name |
+------+------------------+
| 2003 | 홈페이지 UI 개선 |
+------+------------------+
mysql> SELECT P.id, P.name
-> FROM project P
-> WHERE P.id IN (
-> SELECT W.proj_id
-> FROM works_on W
-> WHERE W.empl_id IN (7, 12)
-> );
+------+------------------+
| id | name |
+------+------------------+
| 2003 | 홈페이지 UI 개선 |
+------+------------------+
| 2000년대생이 없는 부서의 id와 이름을 출력하라
NOT EXISTS를 사용하면 다음과 같다.
mysql> SELECT D.id, D.name
-> FROM department D
-> WHERE NOT EXISTS (
-> SELECT *
-> FROM employee E
-> WHERE E.dept_id = D.id AND E.birth_date >= '2000-01-01');
+------+-------------+
| id | name |
+------+-------------+
| 1003 | development |
| 1001 | headquater |
| 1002 | hr |
| 1005 | product |
+------+-------------+
mysql> SELECT D.id, D.name
-> FROM department D
-> WHERE D.id NOT IN (
-> SELECT E.dept_id
-> FROM employee E
-> WHERE E.birth_date >= '2000-01-01');
+------+-------------+
| id | name |
+------+-------------+
| 1003 | development |
| 1001 | headquater |
| 1002 | hr |
| 1005 | product |
+------+-------------+
v 비교연산자 ANY (subquery)
: subquery가 반환하는 결과들 중에서 단 하나라도 v와 비교 연산한 값이 true라면 true를 반환한다.
SOME도 ANY와 같은 역할을 한다.
| 리더보다 높은 연봉을 받는 부서원을 가진 리더의 id,이름,연봉을 출력하라
mysql> SELECT E.id, E.name, E.salary
-> FROM department D, employee E
-> WHERE D.leader_id = E.id AND E.salary < ANY (
-> SELECT salary
-> FROM employee
-> WHERE id <> D.leader_id AND dept_id = E.dept_id);
+----+--------+----------+
| id | name | salary |
+----+--------+----------+
| 3 | JENNY | 50000000 |
| 13 | JISUNG | 90000000 |
+----+--------+----------+
ANY 다음에 오는 subquery의 결과와 E.salary를 비교한다. 비교연산자에 만족하는 결과값이 하나라도 있다면 true를 반환한다.
위의 결과는 JENNY, JISUNG이 이끄는 부서에서 JENNY, JISUNG보다 연봉이 높은 부서원이 있다는 뜻이 된다. 그렇다면 그 부서원의 연봉이 얼마인지도 출력하려면 다음과 같다.
mysql> SELECT E.id, E.name, E.salary, (
-> SELECT max(salary)
-> FROM employee
-> WHERE dept_id = E.dept_id
-> ) AS dept_max_salary
-> FROM department D, employee E
-> WHERE D.leader_id = E.id AND E.salary < ANY (
-> SELECT salary
-> FROM employee
-> WHERE id <> D.leader_id AND dept_id = E.dept_id);
+------+--------+----------+-----------------+
| id | name | salary | dept_max_salary |
+------+--------+----------+-----------------+
| 3 | JENNY | 50000000 | 180000000 |
| 13 | JISUNG | 90000000 | 170000000 |
+------+--------+----------+-----------------+
v 비교연산자 ALL (subquery)
: subquery의 결과들과 v를 비교해서 모두 참이라면 true를 반환한다.| id가 13인 임직원과 한번도 같은 프로젝트에 참여하지 못한 임직원들의 id,이름,직군을 출력하라
mysql> SELECT DISTINCT E.id, E.name, E.position
-> FROM employee E, works_on W
-> WHERE E.id = W.empl_id AND W.proj_id <> ALL (
-> SELECT proj_id
-> FROM works_on
-> WHERE empl_id = 13);
+----+---------+----------+
| id | name | position |
+----+---------+----------+
| 1 | MESSI | DEV_BACK |
| 2 | JANE | DSGN |
| 3 | JENNY | DEV_BACK |
| 6 | JULIA | CFO |
| 7 | MINA | DSGN |
| 9 | HENRY | HR |
| 11 | SUZANNE | PO |
| 12 | CURRY | PLN |
+----+---------+----------+