最外层查询的结果集会返回给调用者,称为外部查询。内部查询的结果是供外部查询使用的,也称为子查询。子查询可以分成独立子查询和相关子查询两类。独立子查询不依赖于它所属的外部查询,而相关子查询则须依赖它所属的外部查询。子查询的期望值可以是单值的、多值的或以表为值。

1.独立子查询

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

1.1 独立标量子查询

标量子查询是返回单个值的子查询,标量子查询可以出现在外部查询中期望使用单个值的任何地方(WHERE、SELECT等等)。示例:查询订单号最大的订单信息

  1. SELECT orderid, orderdate, empid, custid
  2. FROM Sales.Orders
  3. WHERE orderid = (SELECT MAX(O.orderid)
  4. FROM Sales.Orders AS O);

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

1.2 独立多值子查询

多值子查询是在一个列中返回多个值的子查询,而不管子查询是不是独立的。一些谓词(比如IN谓词)可以处理多值子查询。

  1. SELECT orderid
  2. FROM Sales.Orders
  3. WHERE empid IN
  4. (SELECT E.empid
  5. FROM HR.Employees AS E
  6. WHERE E.lastname LIKE N'D%');

其实对于以上需求,同样可以用联接代替子查询来完成这个任务。

  1. SELECT O.orderid
  2. FROM HR.Employees AS E
  3. JOIN Sales.Orders AS O
  4. ON E.empid = O.empid
  5. WHERE E.lastname LIKE N'D%';

2.相关子查询

相关子查询是指引用了外部查询中出现的表的列的子查询,这就意味着子查询要依赖于外部查询。

2.1 EXISTS谓词

支持一个名为EXISTS的谓词,它的输入是一个子查询,如果子查询能够返回任何行,该谓词则返回TRUE,否则返回FALSE。以下查询语句返回下过订单的西班牙客户:

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

3 高级子查询

3.1 返回前一个或后一个记录

假设现在要对Orders表进行查询,对于每个订单,返回当前订单的信息和它前一个订单的信息。"前一个"概念可以用"小于当前值的最大值"来进行表述。类似的"后一个"概念可以用"大于当前值的最小值"来进行表述。

  1. SELECT orderid, orderdate, empid, custid,
  2. (SELECT MAX(O2.orderid)
  3. FROM Sales.Orders AS O2
  4. WHERE O2.orderid < O1.orderid) AS prevorderid
  5. FROM Sales.Orders AS O1;

3.2 连续聚合(Running Aggregate)

连续聚合是一种对累积数据(通常是时间顺序)执行的聚合,首先定义一个视图Sales.OrderTotalsByYear,这个视图包含每年的总订货量。

假设现在有个任务,需要返回每年的订单年份、订货量以及连续几年的总订货量。也就是每一年返回截止到当年的订货量的总和。以下是解决方案的查询语句:

  1. SELECT orderyear, qty,
  2. (SELECT SUM(O2.qty)
  3. FROM Sales.OrderTotalsByYear AS O2
  4. WHERE O2.orderyear <= O1.orderyear) AS runqty
  5. FROM Sales.OrderTotalsByYear AS O1
  6. ORDER BY orderyear;

3.3 行为不当(Misbehaving)的子查询

这部分将介绍几种子查询的运行结果可能与你期望的结果恰好相反的情况,以及为了避免在代码中发生与这些情况有关的逻辑缺陷而应该执行的最佳实践。

NULL的问题

考虑以下看起来很直观的查询,它原本是想返回没有下过订单的客户:

  1. SELECT custid, companyname
  2. FROM Sales.Customers AS C
  3. WHERE custid NOT IN(SELECT O.custid
  4. FROM Sales.Orders AS O);

考虑一种情况,假如Sales.Orders表中出现一行custid为NULL的记录,这时WHERE条件语句返回的所有结果都是UnKnown,所以最后查询出现的结果集为空。

那么,有什么可以执行的实践原则能避免这个问题呢?

1.当一个列不应该允许为NULL时,把它定义为NOT NULL很重要。加强数据的完整性定义,比很多人相像的要重要得多。

2.在你写的所有查询语句中,应该考虑三值逻辑可能出现的三种真值。明确地考虑一下是否要处理NULL值,如果要处理,对NULL值的默认处理是否适合需要,当不适合时,就要对查询语句进行调整。显式地排除NULL值的一个例子就是在子查询中添加谓词O.custid is not null,如下所示:

  1. SELECT custid, companyname
  2. FROM Sales.Customers AS C
  3. WHERE custid NOT IN(SELECT O.custid
  4. FROM Sales.Orders AS O
  5. WHERE O.custid IS NOT NULL);

隐式地排除NULL值的一个例子是使用NOT EXISTS谓词取代NOT IN谓词,如下所示:

  1. SELECT custid, companyname
  2. FROM Sales.Customers AS C
  3. WHERE NOT EXISTS
  4. (SELECT *
  5. FROM Sales.Orders AS O
  6. WHERE O.custid = C.custid);

SQL SERVER技术内幕之4 子查询的更多相关文章

  1. SQL SERVER技术内幕之6 集合查询

    1.定义 集合运算会对两个输入查询的结果集进行逐行比较,根据比较结果和所使用的集合运算来确定某一行是否应该包含在集合运算的结果中.因为集合运算是针对集合之间进行的计算,所以集合运算涉及的两个查询不能包 ...

  2. SQL SERVER技术内幕之3 联接查询

    JOIN表运算符对两个输入表进行操作.联接有三种基本类型:交叉联接.内联接和外联接.这三种联接的区别是它们采用的逻辑查询处理步骤各不相同,每种联接都有一套不同的步骤.交叉联接只有一个步骤----笛卡尔 ...

  3. SQL Server技术内幕笔记合集

    SQL Server技术内幕笔记合集 发这一篇文章主要是方便大家找到我的笔记入口,方便大家o(∩_∩)o Microsoft SQL Server 6.5 技术内幕 笔记http://www.cnbl ...

  4. SQL Server中INNER JOIN与子查询IN的性能测试

    这个月碰到几个人问我关于"SQL SERVER中INNER JOIN 与 IN两种写法的性能孰优孰劣?"这个问题.其实这个概括起来就是SQL Server中INNER JOIN与子 ...

  5. SQL Server进阶(五)子查询

    概述 子查询的概念: 当一个查询是另一个查询的条件时,称之为子查询.子查询可以嵌套在主查询中所有位置,包括SELECT.FROM.WHERE.GROUP BY.HAVING.ORDER BY. 外面的 ...

  6. SQL SERVER技术内幕之10 可编程对象

    一.变量 变量用于临时保存数据值,以供在声明它们的同一批处理语句中引用.例如,以下代码先声明一个数据类型为INT的变量@i,再将它赋值为10; DECLARE @i as INT; SET @i = ...

  7. SQL SERVER技术内幕之5 表表达式

    表表达式是一种命名的查询表达式,代表一个有效的关系表.可以像其他表一样,在数据处理语句中使用表表达式.SQL Server支持4种类型的表表达式:派生表(derived table).公用表表达式(C ...

  8. SQL SERVER技术内幕之10 事务并发

    1.事务 1.1事务的定义 事务是作为单个工作单元而执行的一系列操作.定义事务边界有显式和隐式两种.显式事务的定义以BEGIN TRAN作为开始,以COMMIT TRAN提交事务,以ROLLBACK ...

  9. SQL SERVER技术内幕之8 分组集

    分组集就是分组(GROUP BY子句)使用的一组属性,在传统的SQL中,一个聚合查询只能定义一个分组集: 假设现在不想生成4个单独的结果集,而是希望生成一个统一的结果集,其中包含所有4个分组集的聚合 ...

随机推荐

  1. Python学习手册之数据封装、类方法、静态方法和属性函数

    在上一篇文章中,我们介绍了 Python 的内部方法.操作符重载和对象生命周期,现在我们介绍 Python 的数据封装.类方法.静态方法和属性函数.查看上一篇文章请点击:https://www.cnb ...

  2. LeetCode-Algorithms 1. 两数之和

    个人练习记录 给定一个整数数组和一个目标值,找出数组中和为目标值的两个数. 你可以假设每个输入只对应一种答案,且同样的元素不能被重复利用. 示例: 给定 nums = [2, 7, 11, 15], ...

  3. epoll 服务端 ET模式

    windows下IOCP, linux下 epoll. epoll模型其实也是一个同步模型,ET是epoll里面的一种模式,叫 边缘触发. 个人理解,类似于 windows下的事件选择模型.代码如下: ...

  4. 优步UBER司机全国各地奖励政策汇总 (2月29日-3月6日)

    滴快车单单2.5倍,注册地址:http://www.udache.com/ 如何注册Uber司机(全国版最新最详细注册流程)/月入2万/不用抢单:http://www.cnblogs.com/mfry ...

  5. 成都Uber优步司机奖励政策(3月22日)

    滴快车单单2.5倍,注册地址:http://www.udache.com/ 如何注册Uber司机(全国版最新最详细注册流程)/月入2万/不用抢单:http://www.cnblogs.com/mfry ...

  6. ShareEntryActivity java.lang.ClassNotFoundException | Android类找不到问题

    错误堆栈: Process: com.mci.smagazine, PID: 23265 java.lang.RuntimeException: Unable to instantiate activ ...

  7. ROS(二)Service通信

    使用自定义的消息类型,实现service方式的节点间双向通信 在package目录下创建msg和srv目录,存放package需要使用的.msg和.srv文件. 在ROS中,message被设计为一种 ...

  8. CLR via c#读书笔记八:泛型

    1.定义泛型类型或方法时,为类型指定的任何变量(比如T)都称为类型参数.使用泛型类型或方法时指定的具体数据类型称为类型实参. 2.System.Collections.Concurrent命名空间提供 ...

  9. 【独家】K8S漏洞报告 | 近期bug fix解读&1.9.11主要bug fix汇总

    *内容提要: 1. Kube-proxy长连接优雅断开机制及IPVS模式实现 2. 10/29--11/19 bug fix汇总分析 3. 1.9.11重要bug fix汇总 在本周的跟踪分析中,以1 ...

  10. 韦大仙--Katalon---一款好用的selenium自动化测试插件

    selenium框架是目前使用较广泛的开源自动化框架,一款好的.基于界面的录制工具对于初学者来说可以快速入门:对于老手来说可以提高开发自动化脚本的效率.我们知道Selenium IDE是一款使用较多的 ...