在本教程中,您将了解SQL子查询以及如何使用子查询来形成灵活的SQL语句。
1. SQL子查询基本
请考虑示例数据库中的以下员工(employees
)和部门(departments
)表:
假设要查找位置ID为1700
的所有员工,可能会想出以下解决方案。
首先,找到位置ID为1700
的所有部门:
SELECT
*
FROM
departments
WHERE
location_id = 1700;
执行上面查询语句,得到以下结果:
+---------------+-----------------+-------------+
| department_id | department_name | location_id |
+---------------+-----------------+-------------+
| 1 | 管理 | 1700 |
| 3 | 采购 | 1700 |
| 9 | 行政人员 | 1700 |
| 10 | 财务 | 1700 |
| 11 | 会计 | 1700 |
+---------------+-----------------+-------------+
5 rows in set
其次,使用上一个查询的部门ID列表,查找属于位置ID为1700
的所有员工:
SELECT
employee_id, first_name, last_name
FROM
employees
WHERE
department_id IN (1 , 3, 8, 10, 11)
ORDER BY first_name , last_name;
执行上面示例代码,得到以下结果:
该解决方案有两个问题。 首先,查询departments
表以检查哪个部门属于位置ID为1700
。
由于数据量较小,可以轻松获得部门列表。 但是,在具有大量数据的实际系统中,可能存在问题。
另一个问题是,只要想找到其它位置的员工,就必须修改查询。
更好的解决方案是使用子查询。 根据定义,子查询是嵌套在另一个查询中的查询,例如:SELECT,INSERT,UPDATE或DELETE语句。 在本教程中,我们将重点介绍与SELECT
语句一起使用的子查询。
在此示例中,可以重写上面的两个查询,如下所示:
SELECT
employee_id, first_name, last_name
FROM
employees
WHERE
department_id IN (SELECT
department_id
FROM
departments
WHERE
location_id = 1700)
ORDER BY first_name , last_name;
放在括号内的查询称为子查询,它也称为内部查询或内部选择。 包含子查询的查询称为外部查询或外部选择。
要执行查询,首先,数据库系统必须执行子查询并将括号之间的子查询替换为其结果 – 位于位置ID为1700
的多个部门ID – 然后执行外部查询。
可以在许多地方使用子查询,例如:
- 使用
IN
或NOT IN
运算符 - 比较运算符中
- 使用
EXISTS
或NOT EXISTS
运算符 - 使用
ANY
或ALL
运算符 - 在
FROM
子句中 - 在
SELECT
子句中
2. SQL子查询示例
下面我们举一些使用子查询来了解它们如何工作的例子。
2.1. 带有IN或NOT IN运算符的SQL子查询
在前面的示例中,已经了解了子查询如何与IN
运算符一起使用。 以下示例使用带有NOT IN
运算符的子查询来查找未找到位置ID为1700
的所有员工:
SELECT
employee_id, first_name, last_name
FROM
employees
WHERE
department_id NOT IN (SELECT
department_id
FROM
departments
WHERE
location_id = 1700)
ORDER BY first_name , last_name;
执行上面查询语句,得到以下结果:
+-------------+------------+-----------+
| employee_id | first_name | last_name |
+-------------+------------+-----------+
| 103 | Alexander | Lee |
| 193 | Britney | Zhao |
| 104 | Bruce | Wong |
| 179 | Charles | Yang |
| 105 | David | Liang |
| 107 | Diana | Chen |
| 204 | Hermann | Wu |
| 126 | Irene | Liu |
| 177 | Jack | Yang |
| 145 | John | Liu |
| 176 | Jonathon | Yang |
| 146 | Karen | Liu |
| 178 | Kimberely | Yang |
| 120 | Matthew | Han |
| 121 | Max | Han |
| 201 | Michael | Zhou |
| 122 | Min | Liu |
| 202 | Pat | Zhou |
| 192 | Sarah | Yang |
| 123 | Shanta | Liu |
| 203 | Susan | Zhou |
| 106 | Valli | Chen |
+-------------+------------+-----------+
22 rows in set
2.2. 带有比较运算符的SQL子查询
以下语法说明了子查询如何与比较运算符一起使用:
comparison_operator (subquery)
比较运算符是这些运算符之一:
- 等于(
=
) - 大于(
>
) - 小于(
<
) - 大于或等于(
>=
) - 小于等于(
<=
) - 不相等(
!=
)或(<>
)
以下示例查找薪水最高的员工:
SELECT
employee_id, first_name, last_name, salary
FROM
employees
WHERE
salary = (SELECT
MAX(salary)
FROM
employees)
ORDER BY first_name , last_name;
执行上面示例代码,得到以下结果:
ORDER BY first_name , last_name;
+-------------+------------+-----------+--------+
| employee_id | first_name | last_name | salary |
+-------------+------------+-----------+--------+
| 100 | Steven | Lee | 24000 |
+-------------+------------+-----------+--------+
1 row in set
在此示例中,子查询返回所有员工的最高薪水,外部查询查找薪水等于最高员工的员工。
以下语句查询所有薪水都高于的平均薪水的员工:
SELECT
employee_id, first_name, last_name, salary
FROM
employees
WHERE
salary > (SELECT
AVG(salary)
FROM
employees);
执行上面查询语句,得到以下结果:
在此示例中,首先,子查询返回所有员工的平均工资。 然后,外部查询使用大于运算符来查找工资大于平均值的所有员工。
2.3. 带有EXISTS或NOT EXISTS运算符的SQL子查询
EXISTS
运算符检查子查询返回的行是否存在。 如果子查询包含任何行,则返回true
。 否则,它返回false
。
EXISTS
运算符的语法如下:
EXISTSE (subquery )
NOT EXISTS
运算符与EXISTS
运算符相反。
NOT EXISTS (subquery)
以下示例查找至少有一名员工的薪水大于10000
的所有部门:
SELECT
department_name
FROM
departments d
WHERE
EXISTS( SELECT
1
FROM
employees e
WHERE
salary > 10000
AND e.department_id = d.department_id)
ORDER BY department_name;
执行上面查询语句,得到以下结果:
同样,以下语句查找所有没有薪水大于10000
的员工的部门:
SELECT
department_name
FROM
departments d
WHERE
NOT EXISTS( SELECT
1
FROM
employees e
WHERE
salary > 10000
AND e.department_id = d.department_id)
ORDER BY department_name;
执行上面查询语句,得到以下结果:
2.4. 带有ALL运算符的SQL子查询
子查询与ALL
运算符一起使用时的语法如下:
comparison_operator ALL (subquery)
如果x
大于子查询返回的每个值,则以下条件的计算结果为true
。
x > ALL (subquery)
例如,假设子查询返回三个值:1
,2
和3
。 如果x
大于3
,则以下条件的计算结果为true
。
x > ALL (1,2,3)
以下查询使用GROUP BY子句和MIN()函数按部门查找最低工资:
SELECT
MIN(salary)
FROM
employees
GROUP BY department_id
ORDER BY MIN(salary) DESC;
执行上面查询语句,得到以下结果:
以下示例查找薪水大于每个部门最低薪水的所有员工:
SELECT
employee_id, first_name, last_name, salary
FROM
employees
WHERE
salary >= ALL (SELECT
MIN(salary)
FROM
employees
GROUP BY department_id)
ORDER BY first_name , last_name;
执行上面查询语句,得到以下结果:
2.5. 带有ANY运算符的SQL子查询
以下是带有ANY
运算符的子查询的语法:
comparison_operator ANY (subquery)
例如,如果x
大于子查询返回的任何值,则以下条件的计算结果为true
。 因此,如果x
大于1
,则条件x> SOME(1,2,3)
的计算结果为true
。
x > ANY (subquery)
请注意,SOME
运算符是ANY
运算符的同义词,因此可以互换使用它们。
以下查询查找薪水大于或等于每个部门的最高薪水的所有员工。
SELECT
employee_id, first_name, last_name, salary
FROM
employees
WHERE
salary >= SOME (SELECT
MAX(salary)
FROM
employees
GROUP BY department_id);
执行上面查询语句,得到以下结果:
+-------------+------------+-----------+--------+
| employee_id | first_name | last_name | salary |
+-------------+------------+-----------+--------+
| 100 | Steven | Lee | 24000 |
| 101 | Neena | Wong | 17000 |
| 102 | Lex | Liang | 17000 |
| 103 | Alexander | Lee | 9000 |
| 104 | Bruce | Wong | 6000 |
| 105 | David | Liang | 4800 |
| 106 | Valli | Chen | 4800 |
| 108 | Nancy | Chen | 12000 |
... ...
| 200 | Jennifer | Zhao | 4400 |
| 201 | Michael | Zhou | 13000 |
| 202 | Pat | Zhou | 6000 |
| 203 | Susan | Zhou | 6500 |
| 204 | Hermann | Wu | 10000 |
| 205 | Shelley | Wu | 12000 |
| 206 | William | Wu | 8300 |
+-------------+------------+-----------+--------+
31 rows in set
在此示例中,子查询查找每个部门中员工的最高薪水。 外部查询查看这些值并确定哪个员工的工资大于或等于按部门划分的任何最高工资。
2.7. FROM子句中的SQL子查询
可以在SELECT
语句的FROM
子句中使用子查询,如下所示:
SELECT
*
FROM
(subquery) AS table_name
在此语法中,表别名是必需的,因为FROM
子句中的所有表都必须具有名称。
请注意,FROM
子句中指定的子查询在MySQL中称为派生表,在Oracle中称为内联视图。
以下语句返回每个部门的平均工资:
SELECT
AVG(salary) average_salary
FROM
employees
GROUP BY department_id;
执行上面查询语句,得到以下结果:
可以将此查询用作FROM
子句中的子查询,以计算部门平均工资的平均值,如下所示:
SELECT
ROUND(AVG(average_salary), 0)
FROM
(SELECT
AVG(salary) average_salary
FROM
employees
GROUP BY department_id) department_salary;
+-------------------------------+
| ROUND(AVG(average_salary), 0) |
+-------------------------------+
| 8536 |
+-------------------------------+
1 row in set
2.8. SELECT子句中的SQL子查询
可以在SELECT
子句中使用表达式的任何位置使用子查询。 以下示例查找所有员工的工资,平均工资以及每个员工的工资与平均工资之间的差值。
SELECT
employee_id,
first_name,
last_name,
salary,
(SELECT
ROUND(AVG(salary), 0)
FROM
employees) average_salary,
salary - (SELECT
ROUND(AVG(salary), 0)
FROM
employees) difference
FROM
employees
ORDER BY first_name , last_name;
执行上面查询语句,得到以下结果 –
+-------------+------------+-----------+--------+----------------+------------+
| employee_id | first_name | last_name | salary | average_salary | difference |
+-------------+------------+-----------+--------+----------------+------------+
| 103 | Alexander | Lee | 9000 | 8060 | 940 |
| 115 | Alexander | Su | 3100 | 8060 | -4960 |
| 114 | Avg | Su | 11000 | 8060 | 2940 |
| 193 | Britney | Zhao | 3900 | 8060 | -4160 |
| 104 | Bruce | Wong | 6000 | 8060 | -2060 |
| 179 | Charles | Yang | 6200 | 8060 | -1860 |
| 109 | Daniel | Chen | 9000 | 8060 | 940 |
| 105 | David | Liang | 4800 | 8060 | -3260 |
... ...
| 192 | Sarah | Yang | 4000 | 8060 | -4060 |
| 123 | Shanta | Liu | 6500 | 8060 | -1560 |
| 205 | Shelley | Wu | 12000 | 8060 | 3940 |
| 116 | Shelli | Zhang | 2900 | 8060 | -5160 |
| 117 | Sigal | Zhang | 2800 | 8060 | -5260 |
| 100 | Steven | Lee | 24000 | 8060 | 15940 |
| 203 | Susan | Zhou | 6500 | 8060 | -1560 |
| 106 | Valli | Chen | 4800 | 8060 | -3260 |
| 206 | William | Wu | 8300 | 8060 | 240 |
+-------------+------------+-----------+--------+----------------+------------+
40 rows in set
通过上面的学习,现在您应该了解SQL子查询是什么,以及如何使用子查询来形成灵活的SQL语句。