zoukankan      html  css  js  c++  java
  • 【T-SQL基础】03.子查询

    本系列【T-SQL基础】主要是针对T-SQL基础的总结。

    【T-SQL基础】01.单表查询-几道sql查询题

    【T-SQL基础】02.联接查询

    【T-SQL基础】03.子查询

    【T-SQL基础】04.表表达式-上篇

    【T-SQL基础】04.表表达式-下篇

    【T-SQL基础】05.集合运算

    【T-SQL基础】06.透视、逆透视、分组集

    【T-SQL基础】07.数据修改

    【T-SQL基础】08.事务和并发

    【T-SQL基础】09.可编程对象

     ----------------------------------------------------------

    【T-SQL进阶】01.好用的SQL TVP~~独家赠送[增-删-改-查]的例子

     ----------------------------------------------------------

    【T-SQL性能调优】01.TempDB的使用和性能问题

    【T-SQL性能调优】02.Transaction Log的使用和性能问题

    【T-SQL性能调优】03.执行计划

    【T-SQL性能调优】04.死锁分析

    持续更新......欢迎关注我!

    练习题:

    1.写一条查询语句,返回Orders表中活动的最后一天生成的所有订单。

    2.查询出拥有订单数量的最多的客户下过的所有订单。

    3.查询出200851号(包括这一天)以后没有处理过订单的雇员。

    4.查询2007年下过订单,而在2008年没有下过订单的客户

    5.查询定购了第12号产品的客户

    概述:

    本篇主要是子查询基础的总结。

     

     关键词解释:

    外部查询:查询结果集返回给调用者

    内部查询:查询结果集返回给外部查询。

    独立子查询:独立子查询独立于其外部查询的子查询,可以单独运行子查询。在逻辑上,独立子查询在执行外部查询之前先执行一次,接着外部查询再使用子查询的结果继续进行查询。

    相关子查询:引用了外部查询中出现的表的子查询,查询要依赖于外部查询,不能独立地调用它。在逻辑上,子查询会为每个外部行单独计算一次。

    标量子查询:返回单个值的子查询。标量子查询可以出现在外部查询中期望使用单个值的任何地方。

    多值子查询:在一个列中

    为什么要使用子查询?

    可以避免在查询解决方案中把操作分成多个步骤,并在变量中保存中间查询结果的需要。

    一、独立子查询

    1.独立标量子查询(查看练习题1,2)

    例子:从HR.Employees表中返回empid最大的员工信息。

    可以分两步:

    a.定义一个变量maxid ,通过独立标量子查询查询出empid最大的员工的empid,然后将这个empid保存到变量@maxid

    b.WHERE条件中过滤出empid = @maxid的记录

    DECLARE @maxid AS INT = ( SELECT    MAX(empid)
                              FROM      HR.Employees
                            )
    SELECT  *
    FROM    HR.Employees
    WHERE   empid = @maxid  

    更简单的方法是嵌套子查询,只需要一条查询语句就可以查询出empid最大的员工信息

    SELECT  *
    FROM    HR.Employees
    WHERE   empid = ( SELECT    MAX(empid)
                      FROM      HR.Employees
                    )

    注意:

    1.对于有效的标量子查询,它的返回值不能超过一个,如果标量子查询返回了多个值,在运行时则可能会失败。

    2.如果标量子查询没有返回任何值,其结果就转换为NULL,和NULL行进行比较得到的是UNKNOWN,查询过滤器不会返回任何让过滤表达式计算结果为UNKNOWN的行。

    2.独立多值子查询(查看练习题3

    (1)多值子查询的语法格式

    <标量表达式> IN ( <多值子查询> )

    例子:返回title包含manager的雇员处理过的订单的信息

    方案一:独立多值子查询

    SELECT  *
    FROM    Sales.Orders
    WHERE   empid IN ( SELECT   empid
                       FROM     HR.Employees
                       WHERE    HR.Employees.title LIKE '%Manager' )
    

    方案二:内联接查询

    SELECT  *
    FROM    Sales.Orders
            INNER JOIN HR.Employees ON Sales.Orders.empid = HR.Employees.empid
    WHERE   HR.Employees.title LIKE '%Manager'

    类似地,很多地方既可以用子查询也可以用联接查询来解决问题。数据库引擎对两种查询的解释有时候是一样的,而在另外一些情况下,对二者的解释则是不同的。可以先用一种查询解决问题,如果性能不行,再尝试用联接替代子查询,或用子查询替代联接。

    3.子查询之distinct关键字

     当我们想要剔除掉子查询中的重复值时,会想到在子查询中不必指定distinct关键字,其实是没有必要的,因为数据库引擎会帮助我们删除重复的值,而不用我们显示指定distinct关键字。

    二、相关子查询

    1.相关子查询

    什么是相关子查询:引用了外部查询中出现的表的列,依赖于外部查询,不能独立地运行子查询。在逻辑上,子查询会为每个外部行单独计算一次。

    例子:查询每个客户返回在他参与活动的最后一天下过的所有订单。

    期望结果:

    影响行数:90

    1.首先用独立标量子查询查询出最大的订单日期,返回给外部查询

    SELECT  MAX(orderdate)
    FROM    sales.Orders AS O2

    2.外部查询用O1.orderdate进行过滤,过滤出等于最大订单日期的订单

    3.因为要查询出每个客户参与的订单,所以将独立标量子查询改成相关子查询,用子查询O2.custid与外查询O1.custid关联。

    对于O1中每一行,子查询负责返回当前客户的最大订单日期。如果O1中某行的订单日期和子查询返回的订单日期匹配,那么O1中的这个订单日期就是当前客户的最大的订单日期,在这种情况下,查询便会返回O1表中的这个行。

    SELECT  MAX(orderdate)
    FROM    sales.Orders AS O2
    WHERE   O2.custid = O1.custid

    综合上面的步骤,得到下面的查询语句:

    SELECT  orderid,orderdate,custid
    FROM    sales.Orders AS O1
    WHERE   O1.orderdate = ( SELECT MAX(orderdate)
                             FROM   sales.Orders AS O2
                             WHERE  O2.custid = O1.custid
                           )

    2.EXISTS谓词(查看练习题45

    1. <外查询WHERE EXISTS ( 子查询 )
    2. 它的输入是一个子查询,:如果子查询能够返回任何行,改谓词则返回TRUE,否则返回FALSE.
    3. 如果子查询查询结果又多条,SQL SERVER引擎查询出一条记录后,就会立即返回,这种处理方式叫做短路处理。
    4. Exist谓词只关心是否存在匹配行,而不考虑SELECT列表中指定的列,所有使用SELECT * FROM TABLE,并没有什么负面影响,但是为了展开*代码的列名会有少少量的开销,但是还是推荐使用*通配符,查询语句应该尽可能保持自然和直观,除非有非常令人信服的理由,才可以牺牲代码在这方面的要求。
    5. NOT EXISTS谓词是EXISTS谓词的反面

    三、练习题

    1.写一条查询语句,返回Orders表中活动的最后一天生成的所有订单。

    期望结果:

    本题考察独立子查询的基本用法,首先用独立子查询返回最后一天的日期,然后外部查询过滤出订单日期等于最后一天的所有订单。

    SELECT  orderid ,
            orderdate ,
            custid ,
            empid
    FROM    Sales.Orders
    WHERE   orderdate = ( SELECT    MAX(orderdate)
                          FROM      Sales.Orders
    
                        )

    2.查询出拥有订单数量的最多的客户下过的所有订单。

    期望结果:

    本题考察独立子查询的用法,和第一题类似,分两个步骤:

    1)先用子查询查询出订单数量最多的客户id

    2)然后将id返回给外部查询,外部查询通过客户id过滤出客户下过的所有订单

    方案一:独立标量子查询

    SELECT  custid ,
            orderid ,
            orderdate ,
            empid
    FROM    Sales.Orders
    WHERE   custid = ( SELECT TOP ( 1 ) WITH TIES
                                O.custid
                       FROM     Sales.Orders AS O
                       GROUP BY custid
                       ORDER BY COUNT(*) DESC
    
                     )

    注意:

    TOP ( 1 ) WITH TIES O.custid

    查找出排序后与第一条记录O.custid相等的所有行

    因为下过订单数最多的客户的总订单数是31,且只有一个客户(custid=71),所以最后的查询结果中只有custid=71的客户下过的所有订单。

    3.查询出200851号(包括这一天)以后没有处理过订单的雇员。

    期望结果:

    本题考察独立子查询的用法,本题也可以采用两步来查询出结果。

    1)首先用子查询返回所有200851号(包括这一天)以后处理过订单的雇员,将这些雇员的empid返回给外部查询

    2)然后外部查询用NOT IN过滤出所有200851号(包括这一天)之后没有处理过订单的雇员

    方案一:独立标量子查询 + NOT IN

    SELECT  *
    FROM    HR.Employees
    WHERE   empid NOT IN ( SELECT   empid
                           FROM     Sales.Orders
                           WHERE    orderdate >= '20080501' )

    4.查询2007年下过订单,而在2008年没有下过订单的客户

    期望输出:

     

    方案一:内联接+独立标量子查询

    1.查询出20070101~20071231所有下过订单的客户集合Collection1

    SELECT DISTINCT C.custid,companyname FROM Sales.Orders O
    	INNER JOIN Sales.Customers AS C ON C.custid = O.custid
    	WHERE (orderdate <= '20071231' AND orderdate >= '20070101')

    2.查询出20080101~20081231所有下过订单的客户结合Collection2

    SELECT C.custid FROM Sales.Orders O
    	INNER JOIN Sales.Customers AS C ON C.custid = O.custid
    	WHERE (orderdate <= '20081231' AND orderdate >= '20080101')

    3.Collection1不包含Collection2的子集就是2007年下过订单而在2008年下过订单的客户

    SELECT DISTINCT C.custid,companyname FROM Sales.Orders O
    	INNER JOIN Sales.Customers AS C ON C.custid = O.custid
    	WHERE (orderdate <= '20071231' AND orderdate >= '20070101')
    	AND C.custid NOT IN 
    (
    	SELECT C.custid FROM Sales.Orders O
    	INNER JOIN Sales.Customers AS C ON C.custid = O.custid
    	WHERE (orderdate <= '20081231' AND orderdate >= '20080101')
    )

    方案二:相关子查询 EXISTS+NOT EXISTS

    1.查询出20070101~20071231所有下过订单的客户集合Collection1

    2.查询出20080101~20081231所有下过订单的客户结合Collection2

    3.Collection1不包含Collection2的子集就是2007年下过订单而在2008年下过订单的客户

    SELECT  C.custid ,
            companyname
    FROM    Sales.Customers AS C
    WHERE   EXISTS ( SELECT *
                     FROM   Sales.Orders AS O
                     WHERE  O.custid = C.custid
                            AND ( orderdate <= '20071231'
                                  AND orderdate >= '20070101'
                                ) )
            AND NOT EXISTS ( SELECT *
                             FROM   Sales.Orders AS O
                             WHERE  O.custid = C.custid
                                    AND ( orderdate <= '20081231'
                                          AND orderdate >= '20080101'
                                        ) )    
    
     
    

    由方案一和方案二,我们可以总结出:INNER JOIN+独立子查询可以用Exists+相关子查询代替

    5.查询订购了第12号产品的客户

    期望结果:

    方案一:内联接多张表

    SELECT DISTINCT
            C.custid ,
            companyname
    FROM    Sales.Customers AS C
            INNER JOIN Sales.Orders AS O ON C.custid = O.custid
            INNER JOIN Sales.OrderDetails AS D ON O.orderid = D.orderid
    WHERE   D.productid = '12'

    方案二:嵌套相关子查询

    SELECT  C.custid ,
            companyname
    FROM    Sales.Customers AS C
    WHERE   EXISTS ( SELECT *
                     FROM   Sales.Orders AS O
                     WHERE  O.custid = C.custid
                            AND EXISTS ( SELECT *
                                         FROM   Sales.OrderDetails AS D
                                         WHERE  D.orderid = O.orderid
                                                AND D.productid = '12' ) )

    参考资料:

    《SQL2008技术内幕:T-SQL语言基础》


    作  者: Jackson0714
    出  处:http://www.cnblogs.com/jackson0714/
    关于作者:专注于微软平台的项目开发。如有问题或建议,请多多赐教!
    版权声明:本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接。
    特此声明:所有评论和私信都会在第一时间回复。也欢迎园子的大大们指正错误,共同进步。或者直接私信
    声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是作者坚持原创和持续写作的最大动力! 

  • 相关阅读:
    84. Largest Rectangle in Histogram (Solution 2)
    84. Largest Rectangle in Histogram (Solution 1)
    73. Set Matrix Zeroes
    【JavaScript】Symbol 静态方法
    【JavaScript】Date
    【JavaScript】Math
    725. Split Linked List in Parts把链表分成长度不超过1的若干部分
    791. Custom Sort String字符串保持字母一样,位置可以变
    508. Most Frequent Subtree Sum 最频繁的子树和
    762. Prime Number of Set Bits in Binary Representation二进制中有质数个1的数量
  • 原文地址:https://www.cnblogs.com/jackson0714/p/TSQLFundamentals_03.html
Copyright © 2011-2022 走看看