jjzjj

什么是 SQL 子查询,如何使用 SQL 子查询

Vin_c° 2023-03-28 原文

本文介绍什么是 SQL 子查询,如何使用它们。子查询常用于 WHERE 子句的 IN 操作符中,以及用来填充计算列。

一、子查询

SELECT 语句是 SQL 的查询。我们迄今为止所看到的所有 SELECT 语句都是简单查询,即从单个数据库表中检索数据的单条语句。

查询(query)

任何 SQL 语句都是查询。但此术语一般指 SELECT 语句。

SQL 还允许创建子查询(subquery),即嵌套在其他查询中的查询。为什么要这样做呢?理解这个概念的最好方法是考察几个例子。

二、利用子查询进行过滤

订单存储在两个表中。每个订单包含订单编号、客户 ID、订单日期,在 Orders 表中存储为一行。各订单的物品存储在相关的 OrderItems 表中。Orders 表不存储顾客信息,只存储顾客 ID。顾客的实际信息存储在 Customers 表中。

现在,假如需要列出订购物品 RGAN01 的所有顾客,应该怎样检索?下面列出具体的步骤。

(1) 检索包含物品 RGAN01 的所有订单的编号。

(2) 检索具有前一步骤列出的订单编号的所有顾客的 ID

(3) 检索前一步骤返回的所有顾客 ID 的顾客信息。

上述每个步骤都可以单独作为一个查询来执行。可以把一条 SELECT 语句返回的结果用于另一条 SELECT 语句的 WHERE 子句。

也可以使用子查询来把 3 个查询组合成一条语句。

第一条 SELECT 语句的含义很明确,它对 prod_idRGAN01 的所有订单物品,检索其 order_num 列。输出列出了两个包含此物品的订单:

SELECT order_num
FROM OrderItems
WHERE prod_id = 'RGAN01';

输出:

order_num
-----------
20007
20008

现在,我们知道了哪个订单包含要检索的物品,下一步查询与订单 2000720008 相关的顾客 ID。利用 如何使用 SQL AND、OR、IN 和 NOT 过滤返回的数据 介绍的 IN 子句,编写如下的 SELECT 语句:

SELECT cust_id
FROM Orders
WHERE order_num IN (20007,20008);

输出:

cust_id
----------
1000000004
1000000005

现在,结合这两个查询,把第一个查询(返回订单号的那一个)变为子查询。请看下面的 SELECT 语句:

SELECT cust_id
FROM Orders
WHERE order_num IN (SELECT order_num
                    FROM OrderItems
                    WHERE prod_id = 'RGAN01');

输出:

cust_id
----------
1000000004
1000000005

SELECT 语句中,子查询总是从内向外处理。在处理上面的 SELECT 语句时,DBMS 实际上执行了两个操作。

首先,它执行下面的查询:

SELECT order_num FROM orderitems WHERE prod_id='RGAN01'

此查询返回两个订单号:2000720008。然后,这两个值以 IN 操作符要求的逗号分隔的格式传递给外部查询的 WHERE 子句。外部查询变成:

SELECT cust_id FROM orders WHERE order_num IN (20007,20008)

可以看到,输出是正确的,与前面硬编码 WHERE 子句所返回的值相同。

提示:格式化 SQL

包含子查询的 SELECT 语句难以阅读和调试,它们在较为复杂时更是如此。如上所示,把子查询分解为多行并进行适当的缩进,能极大地简化子查询的使用。

顺便一提,这就是颜色编码起作用的地方,好的 DBMS 客户端正是出于这个原因使用了颜色代码 SQL。

现在得到了订购物品 RGAN01 的所有顾客的 ID。下一步是检索这些顾客 ID 的顾客信息。检索两列的 SQL 语句为:

SELECT cust_name, cust_contact
FROM Customers
WHERE cust_id IN (1000000004,1000000005);

可以把其中的 WHERE 子句转换为子查询,而不是硬编码这些顾客 ID

SELECT cust_name, cust_contact
FROM Customers
WHERE cust_id IN (SELECT cust_id
                  FROM Orders
                  WHERE order_num IN (SELECT order_num
                                      FROM OrderItems
                                      WHERE prod_id = 'RGAN01'));

输出:

cust_name                         cust_contact
-----------------------------     --------------------
Fun4All                           Denise L. Stephens
The Toy Store                     Kim Howard

为了执行上述 SELECT 语句,DBMS 实际上必须执行三条 SELECT 语句。最里边的子查询返回订单号列表,此列表用于其外面的子查询的 WHERE 子句。

外面的子查询返回顾客 ID 列表,此顾客 ID 列表用于最外层查询的 WHERE 子句。最外层查询返回所需的数据。

可见,在 WHERE 子句中使用子查询能够编写出功能很强且很灵活的 SQL 语句。对于能嵌套的子查询的数目没有限制,不过在实际使用时由于性能的限制,不能嵌套太多的子查询。

注意:只能是单列

作为子查询的 SELECT 语句只能查询单个列。企图检索多个列将返回错误。

注意:子查询和性能

这里给出的代码有效,并且获得了所需的结果。但是,使用子查询并不总是执行这类数据检索的最有效方法。更多的论述,请参阅 如何使用 SQL INNER JOIN 联结两个或多个表,其中将再次给出这个例子。

三、作为计算字段使用子查询

使用子查询的另一方法是创建计算字段。假如需要显示 Customers 表中每个顾客的订单总数。订单与相应的顾客 ID 存储在 Orders 表中。

执行这个操作,要遵循下面的步骤:

(1) 从 Customers 表中检索顾客列表;

(2) 对于检索出的每个顾客,统计其在 Orders 表中的订单数目。

正如前两课所述,可以使用 SELECT COUNT(*) 对表中的行进行计数,并且通过提供一条 WHERE 子句来过滤某个特定的顾客 ID,仅对该顾客的订单进行计数。

例如,下面的代码对顾客 1000000001 的订单进行计数:

SELECT COUNT(*) AS orders
FROM Orders
WHERE cust_id = 1000000001;

要对每个顾客执行 COUNT(*),应该将它作为一个子查询。请看下面的代码:

SELECT cust_name,
       cust_state,
       (SELECT COUNT(*)
        FROM Orders
        WHERE Orders.cust_id = Customers.cust_id) AS orders
FROM Customers
ORDER BY cust_name;

输出:

cust_name                     cust_state     orders
-------------------------     ----------     ------
Fun4All                       IN             1
Fun4All                       AZ             1
Kids Place                    OH             0
The Toy Store                 IL             1
Village Toys                  MI             2

这条 SELECT 语句对 Customers 表中每个顾客返回三列:cust_namecust_stateorders

orders 是一个计算字段,它是由圆括号中的子查询建立的。该子查询对检索出的每个顾客执行一次。在此例中,该子查询执行了 5 次,因为检索出了 5 个顾客。

子查询中的 WHERE 子句与前面使用的 WHERE 子句稍有不同,因为它使用了完全限定列名,而不只是列名(cust_id)。它指定表名和列名(Orders.cust_idCustomers.cust_id)。

下面的 WHERE 子句告诉 SQL,比较 Orders 表中的 cust_id 和当前正从 Customers 表中检索的 cust_id

WHERE Orders.cust_id = Customers.cust_id

用一个句点分隔表名和列名,在有可能混淆列名时必须使用这种语法。在这个例子中,有两个 cust_id 列:一个在 Customers 中,另一个在 Orders 中。如果不采用完全限定列名,DBMS 会认为要对 Orders 表中的 cust_id 自身进行比较。因为

SELECT COUNT(*) FROM Orders WHERE cust_id = cust_id

总是返回 Orders 表中订单的总数,而这个结果不是我们想要的:

SELECT cust_name,
       cust_state,
       (SELECT COUNT(*)
        FROM Orders
        WHERE cust_id = cust_id) AS orders
FROM Customers
ORDER BY cust_name;

输出:

cust_name                     cust_state     orders
-------------------------     ----------     ------
Fun4All                       IN             5
Fun4All                       AZ             5
Kids Place                    OH             5
The Toy Store                 IL             5
Village Toys                  MI             5

虽然子查询在构造这种 SELECT 语句时极有用,但必须注意限制有歧义的列。

注意:完全限定列名

你已经看到了为什么要使用完全限定列名,没有具体指定就会返回错误结果,因为 DBMS 会误解你的意思。

有时候,由于出现冲突列名而导致的歧义性,会引起 DBMS 抛出错误信息。

例如,WHEREORDER BY 子句指定的某个列名可能会出现在多个表中。

好的做法是,如果在 SELECT 语句中操作多个表,就应使用完全限定列名来避免歧义。

提示:不止一种解决方案

正如本文前面所述,虽然这里给出的样例代码运行良好,但它并不是解决这种数据检索的最有效方法。在 如何使用 SQL INNER JOIN 联结两个或多个表SQL 如何使用自联结、自然联结和外联结 学习 JOIN 时,我们还会遇到这个例子。

四、小结

本文介绍了什么是子查询,如何使用它们。子查询常用于 WHERE 子句的 IN 操作符中,以及用来填充计算列。我们举了这两种操作类型的例子。

原文链接:https://www.developerastrid.com/sql/sql-subqueries/

(完)

有关什么是 SQL 子查询,如何使用 SQL 子查询的更多相关文章

  1. ruby - 如何使用 Nokogiri 的 xpath 和 at_xpath 方法 - 2

    我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div

  2. ruby - 如何从 ruby​​ 中的字符串运行任意对象方法? - 2

    总的来说,我对ruby​​还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用

  3. ruby - 使用 RubyZip 生成 ZIP 文件时设置压缩级别 - 2

    我有一个Ruby程序,它使用rubyzip压缩XML文件的目录树。gem。我的问题是文件开始变得很重,我想提高压缩级别,因为压缩时间不是问题。我在rubyzipdocumentation中找不到一种为创建的ZIP文件指定压缩级别的方法。有人知道如何更改此设置吗?是否有另一个允许指定压缩级别的Ruby库? 最佳答案 这是我通过查看ruby​​zip内部创建的代码。level=Zlib::BEST_COMPRESSIONZip::ZipOutputStream.open(zip_file)do|zip|Dir.glob("**/*")d

  4. ruby - 为什么我可以在 Ruby 中使用 Object#send 访问私有(private)/ protected 方法? - 2

    类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc

  5. ruby-on-rails - 使用 Ruby on Rails 进行自动化测试 - 最佳实践 - 2

    很好奇,就使用ruby​​onrails自动化单元测试而言,你们正在做什么?您是否创建了一个脚本来在cron中运行rake作业并将结果邮寄给您?git中的预提交Hook?只是手动调用?我完全理解测试,但想知道在错误发生之前捕获错误的最佳实践是什么。让我们理所当然地认为测试本身是完美无缺的,并且可以正常工作。下一步是什么以确保他们在正确的时间将可能有害的结果传达给您? 最佳答案 不确定您到底想听什么,但是有几个级别的自动代码库控制:在处理某项功能时,您可以使用类似autotest的内容获得关于哪些有效,哪些无效的即时反馈。要确保您的提

  6. ruby - 在 Ruby 中使用匿名模块 - 2

    假设我做了一个模块如下:m=Module.newdoclassCendend三个问题:除了对m的引用之外,还有什么方法可以访问C和m中的其他内容?我可以在创建匿名模块后为其命名吗(就像我输入“module...”一样)?如何在使用完匿名模块后将其删除,使其定义的常量不再存在? 最佳答案 三个答案:是的,使用ObjectSpace.此代码使c引用你的类(class)C不引用m:c=nilObjectSpace.each_object{|obj|c=objif(Class===objandobj.name=~/::C$/)}当然这取决于

  7. ruby - 使用 ruby​​ 和 savon 的 SOAP 服务 - 2

    我正在尝试使用ruby​​和Savon来使用网络服务。测试服务为http://www.webservicex.net/WS/WSDetails.aspx?WSID=9&CATID=2require'rubygems'require'savon'client=Savon::Client.new"http://www.webservicex.net/stockquote.asmx?WSDL"client.get_quotedo|soap|soap.body={:symbol=>"AAPL"}end返回SOAP异常。检查soap信封,在我看来soap请求没有正确的命名空间。任何人都可以建议我

  8. python - 如何使用 Ruby 或 Python 创建一系列高音调和低音调的蜂鸣声? - 2

    关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。

  9. ruby-on-rails - 如何验证 update_all 是否实际在 Rails 中更新 - 2

    给定这段代码defcreate@upgrades=User.update_all(["role=?","upgraded"],:id=>params[:upgrade])redirect_toadmin_upgrades_path,:notice=>"Successfullyupgradeduser."end我如何在该操作中实际验证它们是否已保存或未重定向到适当的页面和消息? 最佳答案 在Rails3中,update_all不返回任何有意义的信息,除了已更新的记录数(这可能取决于您的DBMS是否返回该信息)。http://ar.ru

  10. ruby-on-rails - Rails - 子类化模型的设计模式是什么? - 2

    我有一个模型:classItem项目有一个属性“商店”基于存储的值,我希望Item对象对特定方法具有不同的行为。Rails中是否有针对此的通用设计模式?如果方法中没有大的if-else语句,这是如何干净利落地完成的? 最佳答案 通常通过Single-TableInheritance. 关于ruby-on-rails-Rails-子类化模型的设计模式是什么?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.co

随机推荐