1、什么是子查询?

子查询是将一个查询语句嵌套在另一个查询语句中。内层查询语句的查询结果,可以作为外层查询语句提供条件。

2、引发子查询的情况?

  • 使用[NOT]IN的子查询
  • 使用比较运算符的子查询(=、>、 <、>=、 <=、 <>、 !=、 <=>)
  • 使用[NOT]EXISTS的子查询
  • 使用ANY|SOME或者ALL的子查询
运算符 ANY SOME ALL
>、>= 最小值 最小值 最大值
<、<= 最大值 最大值 最小值
= 任意值 任意值
<>、!= 任意值

使用[NOT] IN的子查询

首先重置一下employeedepartment表:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
mysql> DROP TABLE employee,department;
Query OK, 0 rows affected (0.01 sec)

mysql> CREATE TABLE IF NOT EXISTS department(
-> id TINYINT UNSIGNED AUTO_INCREMENT KEY,
-> depName VARCHAR(20) NOT NULL UNIQUE
-> )ENGINE=INNODB;
Query OK, 0 rows affected (0.03 sec)

mysql> INSERT department(depName) VALUES('教学部'),
-> ('市场部'),
-> ('运营部'),
-> ('督导部');
Query OK, 4 rows affected (0.00 sec)
Records: 4 Duplicates: 0 Warnings: 0

mysql> CREATE TABLE IF NOT EXISTS employee(
-> id SMALLINT UNSIGNED AUTO_INCREMENT KEY,
-> username VARCHAR(20) NOT NULL UNIQUE,
-> depId TINYINT UNSIGNED
-> )ENGINE=INNODB;
Query OK, 0 rows affected (0.01 sec)

mysql> INSERT employee(username,depId) VALUES('king',1),
-> ('queen',2),
-> ('张三',3),
-> ('李四',4),
-> ('王五',1);
Query OK, 5 rows affected (0.00 sec)
Records: 5 Duplicates: 0 Warnings: 0

例如我们想查看部门下面的员工,会分别执行以下命令:

1
2
SELECT * FROM department;
SELECT id,username FROM employee WHERE depId IN (1,2,3,4);

通过第一个语句查询到部门的depId然后用IN来查询,而现在我们可以将两条语句整合成一条语句,查询结果是一样的,同样的NOT IN也是一样的:

1
SELECT id,username FROM employee WHERE depId IN(SELECT id FROM department);

使用比较运算符的子查询

创建学员表student和奖学金表scholarship表插入几条测试数据:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
mysql> # 创建学员表student
mysql> CREATE TABLE IF NOT EXISTS student(
-> id TINYINT UNSIGNED AUTO_INCREMENT KEY,
-> username VARCHAR(20) NOT NULL UNIQUE,
-> score TINYINT UNSIGNED
-> );
Query OK, 0 rows affected (0.01 sec)

mysql> INSERT student(username,score) VALUES('king',95),
-> ('king1',35),
-> ('king2',45),
-> ('king3',55),
-> ('king4',65),
-> ('king5',75),
-> ('king6',80),
-> ('king7',90),
-> ('king8',25);
Query OK, 9 rows affected (0.00 sec)
Records: 9 Duplicates: 0 Warnings: 0

mysql>
mysql> # 创建奖学金scholarship
mysql> CREATE TABLE IF NOT EXISTS scholarship(
-> id TINYINT UNSIGNED AUTO_INCREMENT KEY,
-> level TINYINT UNSIGNED
-> );
Query OK, 0 rows affected (0.01 sec)

mysql> INSERT scholarship(level) VALUES(90),(80),(70);
Query OK, 3 rows affected (0.00 sec)
Records: 3 Duplicates: 0 Warnings: 0

假如我们查询获得一等奖学金的同学,之前我们会这样写:

1
2
SELECT level FROM scholarship WHERE id=1;
SELECT id,username FROM student WHERE score>=90;

而用了我们的子查询后将两条语句合并在一起:

1
2
3
4
5
6
7
8
mysql> SELECT id,username FROM student WHERE score>=(SELECT level FROM scholarship WHERE id=1);
+----+----------+
| id | username |
+----+----------+
| 1 | king |
| 8 | king7 |
+----+----------+
2 rows in set (0.01 sec)

将子查询放在了括号内,一定是由内向外执行,将子查询的结果当做条件进行查询。

使用[NOT]EXISTS的子查询

[NOT]EXISTS引发子查询是真假条件,如果子查询是假,那么他的外层条件也不会执行,例如我们只有4个部分,那我们查询第五个部门中的员工的话肯定是没有的:

1
SELECT * FROM department WHERE id=5;

当然,都没有第五个部门就没有必要查询员工,而用EXISTS的查询可以这么写:

1
2
mysql> SELECT id,username FROM employee WHERE EXISTS(SELECT * FROM department WHERE id=5);
Empty set (0.00 sec)

使用ANY|SOME或者ALL的子查询

例如我们查询所有获得奖学金的学员,首先查一下奖学金:

1
2
3
4
5
6
7
8
9
mysql> SELECT * FROM scholarship;
+----+-------+
| id | level |
+----+-------+
| 1 | 90 |
| 2 | 80 |
| 3 | 70 |
+----+-------+
3 rows in set (0.00 sec)

可以看到分数只有大于等于70才能获得奖学金,那么我们利用ANY或者SOME可以查到其中的最小值:

1
2
3
4
5
6
7
8
9
10
mysql> SELECT id,username,score FROM student WHERE score>=ANY(SELECT level FROM scholarship);
+----+----------+-------+
| id | username | score |
+----+----------+-------+
| 1 | king | 95 |
| 6 | king5 | 75 |
| 7 | king6 | 80 |
| 8 | king7 | 90 |
+----+----------+-------+
4 rows in set (0.00 sec)

ALL代表大于里面的最大值,例如查询获得一等奖学金的学员:

1
2
3
4
5
6
7
8
mysql> SELECT id,username,score FROM student WHERE score >=ALL(SELECT level FROM scholarship);
+----+----------+-------+
| id | username | score |
+----+----------+-------+
| 1 | king | 95 |
| 8 | king7 | 90 |
+----+----------+-------+
2 rows in set (0.00 sec)

<=是一样的效果,=!=就相当于INNOT IN

1
2
3
4
5
6
7
8
mysql> SELECT id,username,score FROM student WHERE score=ANY(SELECT level FROM scholarship);
+----+----------+-------+
| id | username | score |
+----+----------+-------+
| 7 | king6 | 80 |
| 8 | king7 | 90 |
+----+----------+-------+
2 rows in set (0.01 sec)

3、将查询结果写入到数据表

1
2
INSERT [INTO] tbl_name [(col_name,...)]
SELECT ...

这个语句是将查询结果插入到另一张表(执行顺序是先查询后插入),我们创建一个测试表将奖学金结果插入:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
mysql> CREATE TABLE test1 (
-> id TINYINT UNSIGNED AUTO_INCREMENT KEY,
-> num TINYINT UNSIGNED
-> );
Query OK, 0 rows affected (0.03 sec)

mysql> INSERT test1(id,num)
-> SELECT id,score FROM student;
Query OK, 9 rows affected (0.01 sec)
Records: 9 Duplicates: 0 Warnings: 0

mysql> SELECT * FROM test1;
+----+------+
| id | num |
+----+------+
| 1 | 95 |
| 2 | 35 |
| 3 | 45 |
| 4 | 55 |
| 5 | 65 |
| 6 | 75 |
| 7 | 80 |
| 8 | 90 |
| 9 | 25 |
+----+------+
9 rows in set (0.00 sec)

4、创建数据表同时将查询结果写入到数据表

1
2
3
CREATE TABLE [IF NOT EXISTS] tbl_name
[(create_definition,...)]
select_statement

这种方式是在建表的时候直接将查询结果插入到表中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
mysql> CREATE TABLE test2 (
-> id TINYINT UNSIGNED AUTO_INCREMENT KEY,
-> score TINYINT UNSIGNED
-> )SELECT id,score FROM student;
Query OK, 9 rows affected (0.02 sec)
Records: 9 Duplicates: 0 Warnings: 0

mysql> SELECT * FROM test2;
+----+-------+
| id | score |
+----+-------+
| 1 | 95 |
| 2 | 35 |
| 3 | 45 |
| 4 | 55 |
| 5 | 65 |
| 6 | 75 |
| 7 | 80 |
| 8 | 90 |
| 9 | 25 |
+----+-------+
9 rows in set (0.01 sec)