介绍
结构化查询语言(SQL)用于管理关系数据库管理系统(RDBMS)中的数据。 SQL 中一个有用的功能是创建查询内的查询,也称为子查询或嵌套查询。 嵌套查询是一个SELECT
语句,通常被括在括号中,并嵌入在主要的SELECT
、INSERT
或DELETE
操作中。
在本教程中,您将使用SELECT
、INSERT
和DELETE
语句与嵌套查询。 您还将在嵌套查询中使用聚合函数,以将数据值与您指定的WHERE
和LIKE
子句中的排序数据值进行比较。
先决条件
要遵循本指南,您需要一台运行某种使用 SQL 的关系数据库管理系统(RDBMS)的计算机。 本教程中的说明和示例已在以下环境中验证:
- A server running Ubuntu 20.04, with a non-root user with
sudo
administrative privileges and firewall enabled. Follow our Initial Server Setup with Ubuntu 20.04 to get started. - MySQL 已在服务器上安装并已安全设置。请按照我们的在 Ubuntu 20.04 上安装 MySQL 的方法进行设置。本指南假定您已按照本指南的第 3 步中的说明设置了一个非根 MySQL 用户。
注意:请注意,许多关系数据库管理系统使用它们自己独特的 SQL 实现。虽然本教程中概述的命令在大多数 RDBMS 上都可以工作,但如果您在除 MySQL 之外的系统上测试它们,确切的语法或输出可能会有所不同。
为了在本教程中练习使用嵌套查询,您需要加载有样本数据的数据库和表。如果您没有准备好要插入的样本,请阅读以下连接到 MySQL 并设置一个样本数据库部分,了解如何创建数据库和表。本教程将在整个过程中引用这个样本数据库和表。
连接到 MySQL 并设置一个样本数据库
如果您的 SQL 数据库运行在远程服务器上,请从本地机器 SSH 进入您的服务器:
接下来,打开 MySQL 提示符,将sammy
替换为您的 MySQL 用户帐户信息:
创建一个名为zooDB
的数据库:
如果数据库成功创建,您将收到以下输出:
OutputQuery OK, 1 row affected (0.01 sec)
要选择 zooDB
数据库,请运行以下 USE
语句:
OutputDatabase changed
选择数据库后,在其中创建一个表。对于本教程的示例,我们将创建一个表,用于存储访问动物园的访客信息。该表将包含以下七个列:
guest_id
:存储访问动物园的访客的值,使用int
数据类型。这也作为表的 主键,意味着此列中的每个值将作为其相应行的唯一标识符。first_name
:使用varchar
数据类型,最大长度为 30 个字符,存储每位访客的名字。last_name
:再次使用varchar
数据类型,最大长度为 30 个字符,存储每位访客的姓氏。guest_type
:使用varchar
数据类型,最大长度为 15 个字符,包含每位访客的类型(成人或儿童)。membership_type
:表示每位访客持有的会员类型,使用varchar
数据类型,最大长度为 30 个字符。membership_cost
:存储各种会员类型的费用。此列使用decimal
数据类型,精度为五,标度为二,意味着此列中的值可以有五位数字,并且小数点右边有两位数字。total_visits
:使用int
数据类型记录每个客人的总访问次数。
通过运行以下 CREATE TABLE
命令创建一个名为 guests
的表,其中包含每列:
接下来,向空表中插入一些示例数据:
OutputQuery OK, 15 rows affected (0.01 sec)
Records: 15 Duplicates: 0 Warnings: 0
插入数据后,您就可以开始在 SQL 中使用嵌套查询了。
使用带有 SELECT
的嵌套查询
在 SQL 中,查询是从数据库表中检索数据的操作,总是包含一个 SELECT
语句。嵌套查询是一个完整的查询,嵌入到另一个操作中。嵌套查询可以具有常规查询中使用的所有元素,并且任何有效的查询都可以嵌入到另一个操作中以成为嵌套查询。例如,嵌套查询可以嵌入到 INSERT
和 DELETE
操作中。根据操作的不同,嵌套查询应该通过将语句置于正确数量的括号中来嵌入,以遵循特定的操作顺序。在想要在一个查询语句中执行多个命令而不是编写多个命令以返回所需结果时,嵌套查询也非常有用。
为了更好地理解嵌套查询,让我们使用上一步的示例数据来说明它们如何有用。例如,假设您想要找出在guests
表中访问动物园次数高于平均次数的所有客人。您可能会认为可以通过以下查询找到这些信息:
然而,使用这种语法的查询将返回错误:
OutputERROR 1111 (HY000): Invalid use of group function
这个错误的原因是,像AVG()
这样的聚合函数除非在SELECT
子句内执行,否则不起作用。
检索这些信息的一种方法是,首先运行一个查询来找出客人访问次数的平均值,然后再运行另一个查询来根据该值找出结果,如以下两个示例:
Output+-----------------+
| avg(total_visits) |
+-----------------+
| 57.5333 |
+-----------------+
1 row in set (0.00 sec)
Output+----------+---------+------------+
| first_name | last_name | total_visits |
+----------+---------+------------+
| Judy | Hopps | 168 |
| Idris | Bogo | 79 |
| Gideon | Grey | 100 |
| Nangi | Reddy | 241 |
| Calvin | Roo | 173 |
+----------+---------+------------+
5 rows in set (0.00 sec)
然而,您可以通过在第二个查询中嵌套第一个查询(SELECT AVG(total_visits) FROM guests;
)来获得相同的结果集。请记住,在嵌套查询中,使用适当数量的括号是必要的,以完成您想要执行的操作。这是因为嵌套查询是首先执行的操作:
Output+------------+-----------+--------------+
| first_name | last_name | total_visits |
+------------+-----------+--------------+
| Judy | Hopps | 168 |
| Idris | Bogo | 79 |
| Gideon | Grey | 100 |
| Nangi | Reddy | 241 |
| Calvin | Roo | 173 |
+------------+-----------+--------------+
5 rows in set (0.00 sec)
根据这个输出,有五位客人的访问次数超过了平均水平。这些信息可以为思考创造性方法提供有用的见解,以确保当前会员继续频繁地访问动物园,并每年续订他们的会员通行证。此外,这个例子展示了使用嵌套查询在一个完整的语句中获得所需结果的价值,而不是需要运行两个单独的查询。
使用嵌套查询与INSERT
通过嵌套查询,您不仅限于将其嵌入到其他SELECT
语句中。事实上,您还可以使用嵌套查询通过将嵌套查询嵌入到INSERT
操作中来向现有表插入数据。
为了说明,假设一家关联的动物园请求一些关于您的客人的信息,因为他们有兴趣为购买其位置的“居民”会员资格的客人提供15%的折扣。为此,请使用CREATE TABLE
创建一个名为upgrade_guests
的新表,该表包含六列。特别注意数据类型,例如int
和varchar
,以及它们可以容纳的最大字符数。如果它们与您在设置示例数据库部分创建的guests
表中的原始数据类型不对齐,则在尝试使用嵌套查询从guests
表插入数据时将收到错误,并且数据将无法正确传输。使用以下信息创建您的表:
为了保持一致性和准确性,我们已将此表中的大部分数据类型信息与guests
表保持一致。我们还删除了不希望出现在新表中的任何额外列。有了这个空表准备好了,下一步是向表中插入所需的数据值。
在此操作中,写入INSERT INTO
和新的upgrade_guests
表,以便清楚指示数据被插入的位置。接下来,使用SELECT
语句编写您的嵌套查询,以检索相关数据值,并使用FROM
确保它们来自guests
表。
另外,通过在嵌套查询语句(membership_cost * 0.85
)中包含乘法数学运算*
,将15%的折扣应用于任何“居民”会员。然后使用WHERE
子句对membership_type
列的值进行排序。您可以进一步缩小范围,只获取“居民”会员的结果,使用LIKE
子句,并在单引号中在“居民”一词前后放置百分比%
符号,以选择符合相同模式或相同措辞的任何会员。您的查询将写成如下形式:
OutputQuery OK, 5 rows affected, 5 warnings (0.01 sec)
Records: 5 Duplicates: 0 Warnings: 5
输出表明,已将五条记录添加到新的upgrade_guests
表中。为了确认您请求的数据已成功从您创建的空upgrade_guests
表中的guests
表转移,并且符合您指定的嵌套查询和WHERE
子句中的条件,请运行以下操作:
Output+----------+------------+------------+-----------------------+-----------------+--------------+
| guest_id | first_name | last_name | membership_type | membership_cost | total_visits |
+----------+------------+------------+-----------------------+-----------------+--------------+
| 1 | Judy | Hopps | Resident Premium Pass | 93.50 | 168 |
| 3 | Duke | Weaselton | Resident Pass | 72.25 | 4 |
| 6 | Jenny | Bellwether | Resident Premium Pass | 93.50 | 20 |
| 10 | Octavia | Otterton | Resident Pass | 72.25 | 11 |
| 11 | Calvin | Roo | Resident Premium Pass | 93.50 | 173 |
+----------+------------+------------+-----------------------+-----------------+--------------+
5 rows in set (0.01 sec)
根据来自您的新upgrade_guests
表的此输出,已正确插入了来自guest
表的与“居民”相关的客户会员信息。另外,已重新计算了带有15%折扣的新membership_cost
。因此,此操作已有助于分段并针对适当的受众,并且已准备好与这些潜在新成员共享折扣价格。
使用嵌套查询与DELETE
为了练习使用嵌套查询与DELETE
语句,假设您想要移除任何经常访问的客人,因为您只想专注于向当前不经常访问动物园的会员推广升级的优惠折扣。
开始这个操作,使用DELETE FROM
语句,以清楚地指明正在删除数据的位置,在本例中是upgrade_guests
表。然后,使用WHERE
子句来筛选任何总访问次数total_visits
超过嵌套查询中指定数量的记录。在您的嵌套查询中,使用SELECT
来查找total_visits
的平均值AVG
,以便前面的WHERE
子句有适当的数据值进行比较。最后,使用FROM
从guests
表中检索该信息。完整的查询语句如下:
OutputQuery OK, 2 rows affected (0.00 sec)
确认这些记录已成功从upgrade_guests
表中删除,并使用ORDER BY
将结果按照total_visits
以数字和升序方式排序:
注意: 使用 DELETE
语句从您的新表中删除记录,不会将其从原始表中删除。您可以运行 SELECT * FROM original_table
来确认所有原始记录都已被记账,即使它们已从您的新表中删除。
Output+----------+------------+------------+-----------------------+-----------------+--------------+
| guest_id | first_name | last_name | membership_type | membership_cost | total_visits |
+----------+------------+------------+-----------------------+-----------------+--------------+
| 3 | Duke | Weaselton | Resident Pass | 72.25 | 4 |
| 10 | Octavia | Otterton | Resident Pass | 72.25 | 11 |
| 6 | Jenny | Bellwether | Resident Premium Pass | 93.50 | 20 |
+----------+------------+------------+-----------------------+-----------------+--------------+
3 rows in set (0.00 sec)
正如此输出所示,DELETE
语句和嵌套查询已正确地删除了指定的数据值。这个表现在保存了那些访问次数少于平均值的三位客人的信息,这是动物园代表与他们联系的绝佳起点,以期望他们以优惠价升级到高级通行证,并希望能鼓励他们更频繁地去动物园。
结论
嵌套查询非常有用,因为它们允许您获得高度精细化的结果,否则您只能通过运行单独的查询来获得。此外,使用嵌套查询的 INSERT
和 DELETE
语句为您提供了另一种一步完成数据插入或删除的方法。如果您想了解更多关于如何组织您的数据的信息,请查看我们的系列文章:如何使用SQL。
Source:
https://www.digitalocean.com/community/tutorials/how-to-use-nested-queries