ZetCode

MySQL 子查询

最后修改于 2023 年 1 月 10 日

在 MySQL 教程的这一部分,我们将介绍 MySQL 中的子查询。

一个子查询是一个查询中的查询。它也被称为内部查询或嵌套查询。子查询可以用于任何允许表达式的地方。它是一个用括号括起来的查询表达式。子查询可以与 SELECT, INSERT, UPDATEDELETE 语句一起使用。

执行 SQL 任务的方法不止一种。许多子查询可以用 SQL 连接来代替。SQL 连接通常更快。

在本章中,我们将使用以下表格

mysql> SELECT * FROM Cars;
+----+------------+--------+
| Id | Name       | Cost   |
+----+------------+--------+
|  1 | Audi       |  52642 |
|  2 | Mercedes   |  57127 |
|  3 | Skoda      |   9000 |
|  4 | Volvo      |  29000 |
|  5 | Bentley    | 350000 |
|  6 | Citroen    |  21000 |
|  7 | Hummer     |  41400 |
|  8 | Volkswagen |  21600 |
+----+------------+--------+

来自 Cars 表的数据。

mysql> SELECT * FROM Customers; SELECT * FROM Reservations;
+------------+-------------+
| CustomerId | Name        |
+------------+-------------+
|          1 | Paul Novak  |
|          2 | Terry Neils |
|          3 | Jack Fonda  |
|          4 | Tom Willis  |
+------------+-------------+
4 rows in set (0.00 sec)

+----+------------+------------+
| Id | CustomerId | Day        |
+----+------------+------------+
|  1 |          1 | 2009-11-22 |
|  2 |          2 | 2009-11-28 |
|  3 |          2 | 2009-11-29 |
|  4 |          1 | 2009-11-29 |
|  5 |          3 | 2009-12-02 |
+----+------------+------------+
5 rows in set (0.00 sec)

我们重述一下我们在 CustomersReservations 表中拥有的内容。子查询通常在具有某种关系的表上执行。

带有 INSERT 语句的子查询

我们想要创建一个 Cars 表的副本。到另一个名为 Cars2 的表中。我们将为此创建一个子查询。

mysql> CREATE TABLE Cars2(Id INT NOT NULL PRIMARY KEY, 
    -> Name VARCHAR(50) NOT NULL, Cost INT NOT NULL);

我们创建一个新的 Cars2 表,该表具有与 Cars 表相同的列和数据类型。要了解表的创建方式,我们可以使用 SHOW CREATE TABLE 语句。

mysql> INSERT INTO Cars2 SELECT * FROM Cars;

这是一个简单的子查询。我们将 Cars 表中的所有行插入到 Cars2 表中。

mysql> SELECT * FROM Cars2;
+----+------------+--------+
| Id | Name       | Cost   |
+----+------------+--------+
|  1 | Audi       |  52642 |
|  2 | Mercedes   |  57127 |
|  3 | Skoda      |   9000 |
|  4 | Volvo      |  29000 |
|  5 | Bentley    | 350000 |
|  6 | Citroen    |  21000 |
|  7 | Hummer     |  41400 |
|  8 | Volkswagen |  21600 |
+----+------------+--------+

数据被复制到一个新的 Cars2 表中。

标量子查询

标量子查询返回一个单一的值。

mysql> SELECT Name FROM Customers WHERE 
    -> CustomerId=(SELECT CustomerId FROM Reservations WHERE Id=5);
+------------+
| Name       |
+------------+
| Jack Fonda |
+------------+

括号中的查询是子查询。它返回一个单一的标量值。然后将返回的值用于外部查询。在这个标量子查询中,我们从 Customers 表中返回客户的姓名,其预订在 Reservations 表中的 Id 等于 5。

表子查询

表子查询返回一个零行或多行的结果表。

mysql> SELECT Name FROM Customers WHERE CustomerId IN    
    -> (SELECT DISTINCT CustomerId FROM Reservations);
+-------------+
| Name        |
+-------------+
| Paul Novak  |
| Terry Neils |
| Jack Fonda  |
+-------------+

上面的查询返回了进行了一些预订的客户的姓名。内部查询从 Reservations 表中返回客户 ID。我们使用 IN 谓词来选择那些客户的姓名,这些客户的 CustomerId 来自内部的 select 查询。

mysql> SELECT DISTINCT Name FROM Customers JOIN Reservations
    -> ON Customers.CustomerId=Reservations.CustomerId;
+-------------+
| Name        |
+-------------+
| Paul Novak  |
| Terry Neils |
| Jack Fonda  |
+-------------+

之前的子查询可以使用 SQL 连接重写。

关联子查询

关联子查询是一个在其 WHERE 子句中使用来自外部查询的值的子查询。该子查询会针对外部查询处理的每一行进行一次评估。

mysql> SELECT Name FROM Cars WHERE Cost <
    -> (SELECT AVG(Cost) FROM Cars);
+------------+
| Name       |
+------------+
| Audi       |
| Mercedes   |
| Skoda      |
| Volvo      |
| Citroen    |
| Hummer     |
| Volkswagen |
+------------+

在上面的关联子查询中,我们返回了所有价格低于表中所有汽车的平均价格的汽车。

带有 EXISTS, NOT EXISTS 的子查询

如果子查询返回任何值,则谓词 EXISTS 返回 TRUENOT EXISTS 返回 FALSE

mysql> SELECT Name FROM Customers WHERE EXISTS
    -> (SELECT * FROM Reservations WHERE
    -> Customers.CustomerId=Reservations.CustomerId);
+-------------+
| Name        |
+-------------+
| Paul Novak  |
| Terry Neils |
| Jack Fonda  |
+-------------+

在上面的 SQL 语句中,我们选择了所有在 Reservations 表中有条目的客户的姓名。

mysql> SELECT Name FROM Customers WHERE NOT EXISTS    
    -> (SELECT * FROM Reservations WHERE 
    -> Customers.CustomerId=Reservations.CustomerId);
+------------+
| Name       |
+------------+
| Tom Willis |
+------------+

在此查询中,我们返回了所有在 Reservations 表中没有条目的客户。这两个 SQL 查询都是关联查询。

MySQL 教程的这一部分专门介绍了 MySQL 子查询。