我们学习了MySQL中如何使用SELECT,INSERT,UPDATE和delete语句进行数据访问和更新操作,在此基础上我们开始子查询。
我们想要查询年龄比‘李’小的学生,要求显示这些学生的信息。
学生的信息我们可以从表中直接查询,但是条件是比年龄小如何实现呢?
1.查找出‘李’的出生日期。
2.利用where语句筛选出生日比‘李’大的学生。
我们可以分成两步实现。
#查询出‘李’的出生日期
SELECT `bornDate` from `student` WHere `studentname`='李';
#利用where语句筛选出生日日期比“李”大的学生
select studentNo,studentName,sex,bornDate,address from `student` where bornDate> `1993-07-23`;
这个语法共用了两个子查询语句;首先通过第一条select 语句从student表中查出‘李’的出生日期,然后利用第二条select语句查询生日日期比‘李’出生日期大的学生纪录,这样就可以查询出数据了。但是有没有简单的方法呢?答案是肯定的。
我们采用新方法:
select studentNo,studentName,sex,bornDate,address
from `student`
where bornDate>
(SELECT `bornDate` from `student` WHere `studentname`='李');
通过这个代码我们可以看到语句已经合并成一句了。这就是子查询。
语法:
SELECT ...... from 表1 where 字段1 比较运算符 (子查询);
其中子查询语句必须放在一对圆括号内,比较运算符包括>,=,<,>=,<=。
习惯上我们成外面为父查询,圆括号中嵌入的查询称为子查询。执行时,先执行子查询部分,求出子查询部分的值,在执行整个父查询,返回最后的结果。
因为子查询作为where条件的一部分,所以还可以和update,INSERT,delete一起使用,语法类似于select语句。
**提示:**在将子查询和比较运算符联合使用,必须保证子查询返回的值不能多于一个。
经验: select语句中使用select * from student
语句的执行效率会低于selectstudentNo,studentName,sex,bornDate,address from
student 因为前者获得表中所有字段所占的资源将大于后者获得的指定字段值所占资源。另外,后者的可维护性高于前者。因此,在编写查询语句时,建议采用以下格式。
SELECT 字段列表 FROM 表名 WHERE 条件表达式;
经验: 我们在进行sql代码编写时,都习惯为表改名。别名是查询语句更加的容易阅读和书写。在select中为表改别名的方法有以下两种。
使用AS 关键字 符合ANSI 标准。
select 字段列表 from 表名 as 表的别名;
使用空格,简便的方法。
select 字段列表 from 表名 表的别名;
当为某个表命名别名后,在select 语句中出现该表的字段需要指定表名是,就必须统一使用该表的别名;否则将产生语法错误。
提示 一般来说,表连接都可以都可以用子查询替换,但是反过来却不一定,有的子查询不能用表连接来替换。子查询比较灵活,方便,形式多样,适合于作用查询的筛选条件,而表连接更适合查看多表数据。
使用IN关键字可以使父查询匹配子查询返回的多个单字段值。
使用=、>等比较运算符时,要求子查询只能返回一条或空的记录。在MySQL 中,当子查询跟随在=、!=、<、<=、>、>= 之后,子查询不允许返回多条记录。出现的错误是“Subquery returns more than 1 row”的意思是“子查询返回值不唯一”。如何解决呢?答案很简单,只要将“=”改为“In”即可。
示例:
select `studentName` from `student`
where `studentNo` In(
select `studentNo` from `result`
where `subjectNo`=(
select `subjectNo` from `subject`
where `subjectName`='logic java'
)and `studentResult`=60
);
如果查询得到没有参加的人呢那就是在 in 之前加上not 关键字。
EXISTS我们在创建表的时候层使用过,它用来检测数据库对象是否存在。
在执行create 或者 drop的时候我们可以使用EXISTS语句判断该数据库对象是否存在,返回值是true还是false。例如我们存在数据表temp,则删除它,然后重新创建。
drop table if exists temp;
除了以上用法之外,EXISTS也可以作为WHERE语句的子查询,基本语法如下。
语法:
SELECT.....FROM 表名 WHERE EXISTS(子查询);
EXISTS 关键字后面的参数是一个任意的子查询,如果该子查询有返回值,则EXISTS子查询的结果为true,此时再执行外层查询语句。如果子查询没有发挥行,则EXISTS子查询结果为false,此时外层语句不再执行查询。
EXISTS和In一样,同样允许添加NOT关键字实现取反操作,NOT EXISTS表示不存在。
提示 EXISTS和NOT EXISTS的结果只取决于是否有返回记录,不取决这些记录的内容,所以EXISTS或NOT EXISTS子查询号SELECT语句中的字段列表通常是无关紧要的。
子查询中常用的运算符
<表达式> [NOT] IN <子查询>
语法说明如下。
<表达式>:用于指定表达式。当表达式与子查询返回的结果集中的某个值相等时,返回 TRUE,否则返回 FALSE;若使用关键字 NOT,则返回的值正好相反。
<子查询>:用于指定子查询。这里的子查询只能返回一列数据。对于比较复杂的查询要求,可以使用 SELECT 语句实现子查询的多层嵌套。
<表达式> {= | < | > | >= | <= | <=> | < > | != }
{ ALL | SOME | ANY} <子查询>
语法说明如下。
<子查询>:用于指定子查询。
<表达式>:用于指定要进行比较的表达式。
ALL、SOME 和 ANY:可选项。用于指定对比较运算的限制。其中,关键字 ALL 用于指定表达式需要与子查询结果集中的每个值都进行比较,当表达式与每个值都满足比较关系时,会返回 TRUE,否则返回 FALSE;关键字 SOME 和 ANY 是同义词,表示表达式只要与子查询结果集中的某个值满足比较关系,就返回 TRUE,否则返回 FALSE。
EXIST <子查询>
若子查询的结果集不为空,则返回 TRUE;否则返回 FALSE。
子查询的应用
【实例 1】在 tb_departments 表中查询 dept_type 为 A 的学院 ID,并根据学院 ID 查询该学院学生的名字,输入的 SQL 语句和执行结果如下所示。
mysql> SELECT name FROM tb_students_info
-> WHERE dept_id IN
-> (SELECT dept_id
-> FROM tb_departments
-> WHERE dept_type= 'A' );
+-------+
| name |
+-------+
| Dany |
| Henry |
| Jane |
| Jim |
| John |
+-------+
5 rows in set (0.01 sec)
上述查询过程可以分步执行,首先内层子查询查出 tb_departments 表中符合条件的学院 ID,单独执行内查询,查询结果如下所示。
mysql> SELECT dept_id
-> FROM tb_departments
-> WHERE dept_type='A';
+---------+
| dept_id |
+---------+
| 1 |
| 2 |
+---------+
2 rows in set (0.00 sec)
可以看到,符合条件的 dept_id 列的值有两个:1 和 2。然后执行外层查询,在 tb_students_info 表中查询 dept_id 等于 1 或 2 的学生的名字。嵌套子查询语句还可以写为如下形式,可以实现相同的效果。
mysql> SELECT name FROM tb_students_info
-> WHERE dept_id IN(1,2);
+-------+
| name |
+-------+
| Dany |
| Henry |
| Jane |
| Jim |
| John |
+-------+
5 rows in set (0.03 sec)
上例说明在处理 SELECT 语句时,MySQL 实际上执行了两个操作过程,即先执行内层子查询,再执行外层查询,内层子查询的结果作为外部查询的比较条件。
【实例 2】与前一个例子类似,但是在 SELECT 语句中使用 NOT IN 关键字,输入的 SQL 语句和执行结果如下所示。
mysql> SELECT name FROM tb_students_info
-> WHERE dept_id NOT IN
-> (SELECT dept_id
-> FROM tb_departments
-> WHERE dept_type='A');
+--------+
| name |
+--------+
| Green |
| Lily |
| Susan |
| Thomas |
| Tom |
+--------+
5 rows in set (0.04 sec)
提示:子查询的功能也可以通过连接查询完成,但是子查询使得 MySQL 代码更容易阅读和编写。
【实例 3】在 tb_departments 表中查询 dept_name 等于“Computer”的学院 id,然后在 tb_students_info 表中查询所有该学院的学生的姓名,输入的 SQL 语句和执行过程如下所示。
mysql> SELECT name FROM tb_students_info
-> WHERE dept_id =
-> (SELECT dept_id
-> FROM tb_departments
-> WHERE dept_name='Computer');
+------+
| name |
+------+
| Dany |
| Jane |
| Jim |
+------+
3 rows in set (0.00 sec)
【实例 4】在 tb_departments 表中查询 dept_name 不等于“Computer”的学院 id,然后在 tb_students_info 表中查询所有该学院的学生的姓名,输入的 SQL 语句和执行过程如下所示。
mysql> SELECT name FROM tb_students_info
-> WHERE dept_id <>
-> (SELECT dept_id
-> FROM tb_departments
-> WHERE dept_name='Computer');
+--------+
| name |
+--------+
| Green |
| Henry |
| John |
| Lily |
| Susan |
| Thomas |
| Tom |
+--------+
7 rows in set (0.00 sec)
【实例 5】查询 tb_departments 表中是否存在 dept_id=1 的供应商,如果存在,就查询 tb_students_info 表中的记录,输入的 SQL 语句和执行结果如下所示。
mysql> SELECT * FROM tb_students_info
-> WHERE EXISTS
-> (SELECT dept_name
-> FROM tb_departments
-> WHERE dept_id=1);
+----+--------+---------+------+------+--------+------------+
| id | name | dept_id | age | sex | height | login_date |
+----+--------+---------+------+------+--------+------------+
| 1 | Dany | 1 | 25 | F | 160 | 2015-09-10 |
| 2 | Green | 3 | 23 | F | 158 | 2016-10-22 |
| 3 | Henry | 2 | 23 | M | 185 | 2015-05-31 |
| 4 | Jane | 1 | 22 | F | 162 | 2016-12-20 |
| 5 | Jim | 1 | 24 | M | 175 | 2016-01-15 |
| 6 | John | 2 | 21 | M | 172 | 2015-11-11 |
| 7 | Lily | 6 | 22 | F | 165 | 2016-02-26 |
| 8 | Susan | 4 | 23 | F | 170 | 2015-10-01 |
| 9 | Thomas | 3 | 22 | M | 178 | 2016-06-07 |
| 10 | Tom | 4 | 23 | M | 165 | 2016-08-05 |
+----+--------+---------+------+------+--------+------------+
10 rows in set (0.00 sec)
由结果可以看到,内层查询结果表明 tb_departments 表中存在 dept_id=1 的记录,因此 EXSTS 表达式返回 TRUE,外层查询语句接收 TRUE 之后对表 tb_students_info 进行查询,返回所有的记录。
EXISTS 关键字可以和条件表达式一起使用。
【实例 6】查询 tb_departments 表中是否存在 dept_id=7 的供应商,如果存在,就查询 tb_students_info 表中的记录,输入的 SQL 语句和执行结果如下所示。
mysql> SELECT * FROM tb_students_info
-> WHERE EXISTS
-> (SELECT dept_name
-> FROM tb_departments
-> WHERE dept_id=7);
Empty set (0.00 sec)
在完成比较复杂的数据查询时,经常会使用到子查询,编写子查询语句时,要注意如下事项。
1)子查询语句可以嵌套在SQL语句中任何表达式出现的位置
在select语句中,子查询可以被嵌套在select语句的列,表和查询条件中,即select子句,from子句,where子句,group by子句和having子句。
下面介绍 子查询在select和from中
嵌套在select语句的select子句的子查询
语法:
select (子查询) from 表名;
**提示:**子查询结果为单列,但不必指定列的别名。
嵌套在select 语句的from子句的子查询
语法:
select * from (子查询) as 表的别名;
注意必须为表指定别名。一般返回多行数据记录,可以当作一张临时表。
2)只出现在子查询中,而没有出现在父查询中的表,不能包含在输出列中。
多层嵌套子查询的最终数据只包含父查询(即最外层的查询)的select子句中出现的字段,而子查询的输出结果通常会作为其外层子查询数据源或用于数据判断匹配。
常见错误:
select * from (select * from rrsult);
这个语句产生错误的原因是主查询语句的from子句是一个子查询语句,因此应该为子查询结果集指定别名。
正确如下:
select * from (select * from result) AS Temp;
CREATE TEMPORARY TABLE 表名(查询语句);
临时表只在当前连接可见,当这个连接关闭时,会自动删除,不会占用数据库空间。修改临时表数据不会影响原表数据不会影响原表数据,因此,如果希望验证某些修改表后的查询,又不想更改原表内容时,可以使用临时表备份原表数据,在临时表上做数据验证。当连接关闭后,系统会自动删除临时表。