• SQL Server进阶(五)子查询


    序言

     为什么需要子查询?

      查看多表的数据也可使用表连接,表连接(join   on...),表连接都可用子查询替换,但有的子查询不能用表连接替换,子查询比较灵活,方便,形式多样,适合于作为查询的筛选条件。

     子查询

      当一个查询是另一个查询的条件时,称之为子查询。外面的查询成为父查询,圆括号嵌入的查询成为称为子查询

      子查询可以嵌套在主查询中所有位置,包括SELECT、FROM、WHERE、GROUP BY、HAVING、ORDER BY。

      SQL Server执行时,先执行子查询部分,求出子查询部分的值,再执行整个父查询,返回最后的结果。

     

    自包含子查询

    自包含标量子查询

    DECLARE @maxid AS INT = (SELECT MAX(orderid)
                             FROM Sales.Orders);
    
    SELECT orderid, orderdate, empid, custid
    FROM Sales.Orders
    WHERE orderid = @maxid;
    SELECT orderid, orderdate, empid, custid
    FROM Sales.Orders
    WHERE orderid = (SELECT MAX(O.orderid)
                     FROM Sales.Orders AS O);

    自包含多值子查询

    SELECT orderid
    FROM Sales.Orders
    WHERE empid = 
      (SELECT E.empid
       FROM HR.Employees AS E
       WHERE E.lastname LIKE N'B%');
    SELECT n
    FROM dbo.Nums
    WHERE n BETWEEN (SELECT MIN(O.orderid) FROM dbo.Orders AS O)
                AND (SELECT MAX(O.orderid) FROM dbo.Orders AS O)
      AND n NOT IN (SELECT O.orderid FROM dbo.Orders AS O);

    相关子查询

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

    例子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:为每个客户返回最大订单ID的订单。

    第一步:

    SELECT MAX(O2.orderid)
    FROM Sales.Orders AS O2

    第二步:

    SELECT MAX(O2.orderid)
       FROM Sales.Orders AS O2
       WHERE O2.custid = O1.custid

    第三步:

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

    高级子查询

     如何表示前一个或后一个记录?逻辑等式:上一个->小于当前值的最大值;下一个->大于当前值的最小值;

    -- 上一个订单ID
    select orderid, orderdate, empid, custid,
    (
    select MAX(o2.orderid) 
    from sales.Orders as o2
    where o2.orderid<o1.orderid
    ) as prevorderid 
    from sales.Orders as o1;

    如何实现连续聚合函数?在子查询中连续计算

    -- 连续聚合
    select orderyear, qty, 
    (select SUM(o2.qty) 
     from sales.OrderTotalsByYear as o2
     where o2.orderyear<=o1.orderyear) as runqty 
    from sales.OrderTotalsByYear as o1
    order by orderyear;

    使用NOT EXISTS谓词取代NOT IN隐式排除NULL值:当对至少返回一个NULL值的子查询使用NOT IN谓词时,外部查询总会返回一个空集。(前面提到,EXISTS谓词采用的是二词逻辑而不是三词逻辑)

    -- 隐式排除NULL值
    select custid,companyname from sales.Customers as c
    where not exists
    (select * 
     from sales.Orders as o
     where o.custid=c.custid);

    又如以下查询请求返回每个客户在2007年下过订单而在2008年没有下过订单的客户:

    select custid, companyname
    from sales.Customers as c
    where exists 
    (select * from sales.Orders as o1
     where c.custid=o1.custid 
     and o1.orderdate>='20070101' and o1.orderdate<'20080101')
    and not exists 
    (select * from sales.Orders as o2
     where c.custid=o2.custid
     and o2.orderdate>='20080101' and o2.orderdate<'20090101');

     

    Exists

      exists是用来判断是否存在的,当exists查询中的查询存在结果时则返回真,否则返回假。not exists则相反。

      exists做为where 条件时,是先对where 前的主查询询进行查询,然后用主查询的结果一个一个的代入exists的查询进行判断,如果为真则输出当前这一条主查询的结果,否则不输出。

      exists后面的查询称为相关子查询,即子查询的查询条件依赖于外层父查询中的某个属性值,其处理过程一般为:先取外层查询中的第一个元组,根据它与内层查询中的相关属性值处理内层查询,若where子句返回true,则将此元组放入结果表中,然后取外层查询中的下一个元组,重复这个过程直到全部检查完毕为止。

      例如:我们有一张人员信息表,里边有一个人员类型Id字段(pTypeId),它是一个外键,对应着人员类型表的主键ptId。如果我们有以下的SQL语句,使用Exists关键字则可以有如下的理解:

    select * from Employee e where exists 
    (select * from EmployeeType et where e.pTypeId=et.ptId)

      那么,在这句SQL的执行过程中,我们可以将其理解为一个双重的for循环,外边是主表的循环遍历,然后将其放到一个temp变量中,再进入从表的for循环,并与从表的项进行一个一个的按照匹配规则(这里是e.pTypeId=et.ptId)进行匹配,如果有匹配成功则返回true,并且将这一行记录放到要返回的结果集中,否则返回false。

     

    SQL中EXISTS的使用

    查询所有选修了“语文”课程的学生名

    普通SQL查询:

     SELECT s.Sname FROM dbo.Student s
      WHERE s.S# IN (SELECT  sc.S# FROM dbo.Sc sc INNER JOIN dbo.Course c ON c.C# =sc.C# WHERE c.Cname ='语文')

    带EXISTS的SQL查询:

    SELECT s.Sname FROM dbo.Student s
      WHERE EXISTS (SELECT  sc.S# FROM dbo.Sc sc INNER JOIN dbo.Course c ON c.C# =sc.C# WHERE c.Cname ='语文' AND s.S# =sc.S#)

    Exists原理

      exists做为where 条件时,是先对where 前的主查询询进行查询,然后用主查询的结果一个一个的代入exists的查询进行判断,如果为真则输出当前这一条主查询的结果,否则不输出。 

      查询时,一般情况下,子查询会分成两种情况:     

    1.子查询与外表的字段有关系时

    select 字段1 , 字段2 from 表1 where exists (select 字段1 , 字段2 from 表2 where 表2.字段2 = 表1.字段2)

      这时候,此SQL语句相当于一个关联查询。

      它先执行表1的查询,然后把表1中的每一条记录放到表2的条件中去查询,如果存在,则显示此条记录。

    2.子查询与外表的字段没有任何关联

    Select 字段1 , 字段2 from 表1 where exists ( select * from 表2 where 表2.字段 = ‘ 条件‘)

      在这种情况下,只要子查询的条件成立,就会查询出表1中的所有记录,反之,如果子查询中没有查询到记录,则表1不会查询出任何的记录。

      当子查询与主表不存在关联关系时,简单认为只要exists为一个条件判断,如果为true,就输出所有记录。如果为false则不输出任何的记录。

    Exists谓词

     带有EXISTS的子查询不返回任何记录的数据,只返回逻辑值“True”或“False”

     返回下订单的西班牙客户

    SELECT custid, companyname
    FROM Sales.Customers AS C
    WHERE country = N'Spain'
      AND EXISTS
        (SELECT * FROM Sales.Orders AS O
         WHERE O.custid = C.custid);

     返回没有下订单的西班牙客户

    SELECT custid, companyname
    FROM Sales.Customers AS C
    WHERE country = N'Spain'
      AND NOT EXISTS
        (SELECT * FROM Sales.Orders AS O
         WHERE O.custid = C.custid);

      对于EXISTS,它采用的是二值逻辑(TRUE和FALSE),它只关心是否存在匹配行,而不考虑SELECT列表中指定的列,并且无须处理所有满足条件的行。可以将这种处理方式看做是一种“短路”,它能够提高处理效率。 

      另外,由于EXISTS采用的是二值逻辑,因此相较于IN要更加安全,可以避免对NULL值得处理。 

    收集资料

    https://blog.csdn.net/qq_26937525/article/details/53930498

    http://www.cnblogs.com/jackson0714/p/TSQLFundamentals_03.html

    https://blog.csdn.net/mascf/article/details/50288199

  • 相关阅读:
    试算平衡
    对账服务
    会计科目与账户
    支付系统难点全面梳理剖析:核算对账核心
    支付系统设计——你不可不知的会计核心(转发整理)
    支付系统设计之查漏补缺的一环:差错账处理
    支付清算业务总结
    财务对账系统v1.0立项到结项的整体回顾
    DNS视图及压力测试(四)
    一个快速检测系统CPU负载的小程序
  • 原文地址:https://www.cnblogs.com/cnki/p/9541706.html
Copyright © 2020-2023  润新知