HTML5技术

那些年我们写过的T-SQL(上) - 熊二哥(5)

字号+ 作者:H5之家 来源:H5之家 2016-02-06 09:04 我要评论( )

外部联接:除了包含内联接的两个逻辑处理阶段,还包含一个"添加外部行"的第三个阶段。外联接包含LEFT OUTER JOIN、RIGHT OUTER JOIN和FULL OUTER JOIN三种类型,分别表示左侧表为保留表、右侧表为保留表和两侧表均

外部联接:除了包含内联接的两个逻辑处理阶段,还包含一个"添加外部行"的第三个阶段。外联接包含LEFT OUTER JOIN、RIGHT OUTER JOIN和FULL OUTER JOIN三种类型,分别表示左侧表为保留表、右侧表为保留表和两侧表均为保留表。这儿的保留表也就是我们常说的基准表,即其中的每一条记录实际上都会在最终的结果中显示出来,之后会通过一个内部行、外部行的概念对其进行阐述。在逻辑处理的第二阶段,也就是筛选阶段,将筛选出符合ON谓词的行,称之为内部行(由内部联接返回)。在以后的第三阶段,将识别出保留表中基于ON谓词未能与另一张表匹配的行,称之为外部行,此阶段会将这些外部行添加到之前的结果集中,在这些外部行中,其非保留表字段将使用NULL作为占位符。

接下来介绍外联接中容易忽视的知识难点,其与之前介绍的查询顺序密切相关,就是在查询中ON关键字和WHERE关键字的区别的问题,通过之前的介绍可知,ON关键字属于FROM字句,其作用时间在WHERE字句之前,也就是说WHERE操作是基于之前结果的,理清逻辑就好理解了。之前提到外联接查询的结果包含内部行和外部行,如果我们想进一步将外部行或内部行剔除,那么就可以使用WHERE进行筛选,需要注意非保留表中的属性一定要选择非NULL属性,这时的查询真正对应到NULL占位符(NULL有两种含义,一种是数据不存在,另一种是联接的占位符),如下所示。

只获取外部行,即订单不存在的用户记录:SELECT c.custid, c.companyname FROM sale.customer AS c LEFT OUTER JOIN sale.order AS o on c.custid = o.custid WHERE o.orderid IS NULL

最后,补充两个使用外联接容易出错的场景。第一个是在一个查询中同时包含内联接和外联接的情况,由于表运算符的处理是有逻辑顺序的(其他为同时操作,之前有介绍),因而不同的联接顺序可能造成不同的结果集,比如在使用LEFT JOIN之后使用INNER JOIN很有可能就会把添加上的外部行再次除去掉,一般推荐确定好保留表的先后顺序,均使用LEFT JOIN为宜。另外一个问题是在有外联接的情况下使用COUNT聚合操作符,会将外部行业作为计数目标,然而有时这是不合理的,此时需要使用指定的非空列,COUNT(col)。

 

SQL支持在查询内编写查询,外部查询返回结果集,内部查询的结果集被外部查询使用,称之为子查询。如果子查询不依赖其所属外部查询,称之为无关子查询,反之为相关子查询。子查询的返回值可以是一个标量、多个值和一张表。

  • 无关子查询

  • 标量子查询 获取当前最大订单的相关信息: SELECT * FROM sale.[order] WHERE orderid = (SELECT MAX(o.orderid) FROM sale.[order] AS o)

    多值子查询 返回中国客户的订单:SELECT * FROM sales.[order] WHERE custid in (SELECT C.custid FROM sales.customer AS c WHERE c.country = N'China')

    无关子查询最为简单常见,在之前的IN谓词查询中,可能会想到不是给custid加上DISCTINCT会更高效,实际上查询分析器会默认考虑删除重复记录,此外多值查询还有ALL、ANY和SOME关键字。

  • 相关子查询

  • 相关子查询引用的列位于外部查询中,该子查询依赖于外部查询,自身无法单独调用。

    获取每个用户返回最大订单ID的订单:SELECT * FROM sales.[order] AS o1 WHERE o1.orderid = (SELECT MAX(o2.orderid) FROM sales.[order] AS o2 WHERE o2.uid = o1.uid)

  • EXISTS谓词

  • 接受一个子查询作为输入,如果子查询返回任意行返回TRUE,否则FALSE,这也是SQL中少有的2值逻辑场景。此外,EXISTS在查询优化方面也有一定应用,在之后优化的专题中会涉及。

    查询没有下订单的美国客户: SELECT * FROM sales.customer AS c WHERE country = 'American' AND NOT EXISTS (SELECT * FROM sales.order AS o WHERE o.custid = c.custid)

     

    是不是觉得上面的部分很简单,那么接下来将带来两个相对复杂的查询场景。

    返回前一个值

    返回每个当前订单及其前一个订单ID的信息,前一个逻辑上等价于"小于当前值中最大的那个值"

    SELECT orderid, orderdate, empid, custid, ( SELECT MAX(T2.orderid) FROM sales.[order] AS T2 WHERE T2.orderid < T1.orderid ) AS prevorderid

    FROM sales.[order] AS T1

    随时间累积值的聚合

    返回每年的订单年度、数量和经过年度的运行总量

    SELECT orderyear, qty, ( SELECT SUM(T2.qty) FROM sales.orderTotalByYear AS T2 WHERE T2.orderyear <= T1.orderyear ) AS runqty

    FROM sales.orderTotalByYear AS T1 ORDER BY orderyear

     

    1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,请转载时务必注明文章作者和来源,不尊重原创的行为我们将追究责任;3.作者投稿可能会经我们编辑修改或补充。

    相关文章
    • 谈一下我们是如何开展code review的 - HarlanC

      谈一下我们是如何开展code review的 - HarlanC

      2017-04-27 15:03

    • 这些年,我身边的那些人和事 - 左潇龙

      这些年,我身边的那些人和事 - 左潇龙

      2017-02-20 12:01

    • EntityFramework Core 1.1有哪些新特性呢?我们需要知道 - JeffckyWang

      EntityFramework Core 1.1有哪些新特性呢?我们需要知道 - JeffckyWa

      2017-01-22 18:01

    • Microsoft Edge与Google Chrome那些不同的举止 - fyter

      Microsoft Edge与Google Chrome那些不同的举止 - fyter

      2017-01-16 12:00

    网友点评
    "