![MySQL 8从入门到精通(视频教学版)](https://wfqqreader-1252317822.image.myqcloud.com/cover/737/26542737/b_26542737.jpg)
7.5 子查询
子查询指一个查询语句嵌套在另一个查询语句内部的查询,这个特性从MySQL 4.1开始引入。在SELECT子句中先计算子查询,子查询结果作为外层另一个查询的过滤条件,查询可以基于一个表或者多个表。子查询中常用的操作符有ANY(SOME)、ALL、IN、EXISTS。子查询可以添加到SELECT、UPDATE和DELETE语句中,而且可以进行多层嵌套。子查询中也可以使用比较运算符,如“<”“<=”“>”“>=”和“!=”等。本节将介绍如何在SELECT语句中嵌套子查询。
7.5.1 带ANY、SOME关键字的子查询
ANY和SOME关键字是同义词,表示满足其中任一条件,它们允许创建一个表达式对子查询的返回值列表进行比较,只要满足内层子查询中的任何一个比较条件,就返回一个结果作为外层查询的条件。
下面定义两个表tbl1和tbl2:
CREATE table tbl1 ( num1 INT NOT NULL); CREATE table tbl2 ( num2 INT NOT NULL);
分别向两个表中插入数据:
INSERT INTO tbl1 values(1), (5), (13), (27); INSERT INTO tbl2 values(6), (14), (11), (20);
ANY关键字接在一个比较操作符的后面,表示若与子查询返回的任何值比较为TRUE,则返回TRUE。
【例7.53】返回tbl2表的所有num2列,然后将tbl1中的num1的值与之进行比较,只要大于num2的任何1个值,即为符合查询条件的结果。
![](https://epubservercos.yuewen.com/76103A/15056702904172406/epubprivate/OEBPS/Images/271.jpg?sign=1739021244-hpLd5srpp49Wh8i2rACkEWhbtTVJzrqM-0-a8e6c0d094e8b22b6d2fe57786c950e7)
在子查询中,返回的是tbl2表的所有num2列结果(6,14,11,20),然后将tbl1中的num1列的值与之进行比较,只要大于num2列的任意一个数即为符合条件的结果。
7.5.2 带ALL关键字的子查询
ALL关键字与ANY和SOME不同,使用ALL时需要同时满足所有内层查询的条件。例如,修改前面的例子,用ALL关键字替换ANY。
ALL关键字接在一个比较操作符的后面,表示与子查询返回的所有值比较为TRUE,则返回TRUE。
【例7.54】返回tbl1表中比tbl2表num2列所有值都大的值,SQL语句如下:
![](https://epubservercos.yuewen.com/76103A/15056702904172406/epubprivate/OEBPS/Images/272.jpg?sign=1739021244-ZjyNMu0mw4T4lVNRG56qBPzBnzNYW7hA-0-22826573254b50556bce6c8c3ce1b7c0)
在子查询中,返回的是tbl2的所有num2列结果(6,14,11,20),然后将tbl1中的num1列的值与之进行比较,大于所有num2列值的num1值只有27,因此返回结果为27。
7.5.3 带EXISTS关键字的子查询
EXISTS关键字后面的参数是一个任意的子查询,系统对子查询进行运算以判断它是否返回行,如果至少返回一行,那么EXISTS的结果为true,此时外层查询语句将进行查询;如果子查询没有返回任何行,那么EXISTS返回的结果是false,此时外层语句将不进行查询。
【例7.55】查询suppliers表中是否存在s_id=107的供应商,如果存在,则查询fruits表中的记录,SQL语句如下:
![](https://epubservercos.yuewen.com/76103A/15056702904172406/epubprivate/OEBPS/Images/273.jpg?sign=1739021244-m7c1fFnvI8faAlIKlwIPi7QYcRmUo1j5-0-6ecdaf658a7999404751557a2894653f)
由结果可以看到,内层查询结果表明suppliers表中存在s_id=107的记录,因此EXISTS表达式返回true;外层查询语句接收true之后对表fruits进行查询,返回所有的记录。
EXISTS关键字可以和条件表达式一起使用。
【例7.56】查询suppliers表中是否存在s_id=107的供应商,如果存在,则查询fruits表中的f_price大于10.20的记录,SQL语句如下:
![](https://epubservercos.yuewen.com/76103A/15056702904172406/epubprivate/OEBPS/Images/274.jpg?sign=1739021244-RVX42QLWlJY0rzqvX8H5uFkndiX95AG9-0-0418126c6793dd1f6ec66d4e08ad945d)
由结果可以看到,内层查询结果表明suppliers表中存在s_id=107的记录,因此EXISTS表达式返回true;外层查询语句接收true之后根据查询条件f_price > 10.20对fruits表进行查询,返回结果为4条f_price大于10.20的记录。
NOT EXISTS与EXISTS使用方法相同,返回的结果相反。子查询如果至少返回一行,那么NOT EXISTS的结果为false,此时外层查询语句将不进行查询;如果子查询没有返回任何行,那么NOT EXISTS返回的结果是true,此时外层语句将进行查询。
【例7.57】查询suppliers表中是否存在s_id=107的供应商,如果不存在则查询fruits表中的记录,SQL语句如下:
![](https://epubservercos.yuewen.com/76103A/15056702904172406/epubprivate/OEBPS/Images/275.jpg?sign=1739021244-LAp6XTtXPfTLp0O0hzStGFTYJC4Drfth-0-a6f4ebadcb3f7d2f11746f3ad8db2a72)
查询语句SELECT s_name FROM suppliers WHERE s_id = 107,对suppliers表进行查询返回了一条记录,NOT EXISTS表达式返回false,外层表达式接收false,将不再查询fruits表中的记录。
提示
EXISTS和NOT EXISTS的结果只取决于是否会返回行,而不取决于这些行的内容,所以这个子查询输入列表通常是无关紧要的。
7.5.4 带IN关键字的子查询
IN关键字进行子查询时,内层查询语句仅仅返回一个数据列,这个数据列里的值将提供给外层查询语句进行比较操作。
【例7.58】在orderitems表中查询f_id为c0的订单号,并根据订单号查询具有订单号的客户c_id,SQL语句如下:
![](https://epubservercos.yuewen.com/76103A/15056702904172406/epubprivate/OEBPS/Images/276.jpg?sign=1739021244-pfX5RsWKbrc65HOlSC4hQZH0FuyFRL4m-0-6ee6ba68013d2c8703ad645caadc945f)
查询结果的c_id有两个值,分别为10001和10004。上述查询过程可以分步执行,首先内层子查询查出orderitems表中符合条件的订单号,单独执行内查询,查询结果如下:
![](https://epubservercos.yuewen.com/76103A/15056702904172406/epubprivate/OEBPS/Images/277.jpg?sign=1739021244-7hXhgwODYnmr5SsqtUgKhWaLCAppoSvv-0-cbd5942bf1b971b9a29edabb707fc592)
可以看到,符合条件的o_num列的值有两个:30003和30005,然后执行外层查询,在orders表中查询订单号等于30003或30005的客户c_id。嵌套子查询语句还可以写为如下形式,实现相同的效果:
![](https://epubservercos.yuewen.com/76103A/15056702904172406/epubprivate/OEBPS/Images/278.jpg?sign=1739021244-QGPrWZXaH3Jk4VKYGluekfUR434PlhAF-0-dc91216ada3f020051ea734448fcf1f6)
这个例子说明在处理SELECT语句的时候,MySQL实际上执行了两个操作过程,即先执行内层子查询,再执行外层查询,内层子查询的结果作为外部查询的比较条件。
SELECT语句中可以使用NOT IN关键字,其作用与IN正好相反。
【例7.59】与前一个例子类似,但是在SELECT语句中使用NOT IN关键字,SQL语句如下:
![](https://epubservercos.yuewen.com/76103A/15056702904172406/epubprivate/OEBPS/Images/279.jpg?sign=1739021244-CC4eNvSCUeSqMw34urA4hALOeJKAFCmH-0-70bcdaab9ab5ebcb458d0c2fe6e9df72)
这里返回的结果有3条记录,由前面可以看到,子查询返回的订单值有两个,即30003和30005,但为什么这里还有值为10001的c_id呢?这是因为c_id等于10001的客户的订单不只一个,可以查看订单表orders中的记录。
![](https://epubservercos.yuewen.com/76103A/15056702904172406/epubprivate/OEBPS/Images/280.jpg?sign=1739021244-mErdNrh3NJaLsOfspij112qcjeudtXp1-0-483bbdc670ba39273ad94d6e45b8a835)
可以看到,虽然排除了订单号为30003和30005的客户c_id,但是o_num为30001的订单与30005都是10001号客户的订单。所以结果中只是排除了订单号,但是仍然有可能选择同一个客户。
提示
子查询的功能也可以通过连接查询完成,但是子查询使得MySQL代码更容易阅读和编写。
7.5.5 带比较运算符的子查询
在前面介绍的带ANY、ALL关键字的子查询时使用了“>”比较运算符,子查询时还可以使用其他的比较运算符,如“<”“<=”“=”“>=”和“!=”等。
【例7.60】在suppliers表中查询s_city等于“Tianjin”的供应商s_id,然后在fruits表中查询所有该供应商提供的水果的种类,SQL语句如下:
SELECT s_id, f_name FROM fruits WHERE s_id = (SELECT s1.s_id FROM suppliers AS s1 WHERE s1.s_city = 'Tianjin');
该嵌套查询首先在suppliers表中查找s_city等于Tianjin的供应商的s_id,单独执行子查询查看s_id的值,执行下面的操作过程:
![](https://epubservercos.yuewen.com/76103A/15056702904172406/epubprivate/OEBPS/Images/281.jpg?sign=1739021244-8eqWeSkzeDtk1rdNAqVUUfcrt7wF3SXg-0-345ba21110f98cfdbdc4328ad39fa446)
然后在外层查询时,在fruits表中查找s_id等于101的供应商提供的水果的种类,查询结果如下:
![](https://epubservercos.yuewen.com/76103A/15056702904172406/epubprivate/OEBPS/Images/282.jpg?sign=1739021244-Llnp7BrBgQTnlPHmFcIIlWrRHzVXd3K0-0-af5647bc82afbffd6c177b1304112c34)
结果表明,“Tianjin”地区的供应商提供的水果种类有3种,分别为“apple”“blackberry”“cherry”。
【例7.61】在suppliers表中查询s_city等于“Tianjin”的供应商s_id,然后在fruits表中查询所有非该供应商提供的水果的种类,SQL语句如下:
![](https://epubservercos.yuewen.com/76103A/15056702904172406/epubprivate/OEBPS/Images/283.jpg?sign=1739021244-AoFiRq0pqjewxpaEubCN3Esx5JPUS8Sq-0-cac5ca49f56e99cd178761b10a0908d7)
该嵌套查询执行过程与前面相同,在这里使用了不等于“<>”运算符,因此返回的结果和前面正好相反。