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>='' and o1.orderdate<'')
and not exists
(select * from sales.Orders as o2
where c.custid=o2.custid
and o2.orderdate>='' and o2.orderdate<'');


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
SQL Server进阶(五)子查询的更多相关文章
- Sql Server系列:子查询
1 子查询概念 子查询是嵌套在另一个查询中的普通T-SQL查询.在有一个SELECT语句通过使用小括号创建子查询,作为另一个查询的部分数据或条件的基础. 子查询通常用于满足以下某个需求: ◊ 将一个查 ...
- SQL Server 基础:子查询
1.子查询的概念:子查询就是嵌套在主查询中的查询.子查询可以嵌套在主查询中所有位置,包括SELECT.FROM.WHERE.GROUP BY.HAVING.ORDER BY.2.子查询的分类:2.1按 ...
- sql server update+select(子查询修改)20190304
if OBJECT_ID('tempdb..##t2') is not null drop table ##t2;create table ##t2( a int, b int, c datetime ...
- c#Winform程序调用app.config文件配置数据库连接字符串 SQL Server文章目录 浅谈SQL Server中统计对于查询的影响 有关索引的DMV SQL Server中的执行引擎入门 【译】表变量和临时表的比较 对于表列数据类型选择的一点思考 SQL Server复制入门(一)----复制简介 操作系统中的进程与线程
c#Winform程序调用app.config文件配置数据库连接字符串 你新建winform项目的时候,会有一个app.config的配置文件,写在里面的<connectionStrings n ...
- SQL Server 进阶 01 数据库的设计
SQL Server 进阶 01 数据库的设计 本篇目录 课程内容回顾及介绍 为什么需要规范的数据库设计 设计数据库的步骤 绘制E-R(实体-关系)图 实体-关系模型 如何将E-R图转换为表 数据规范 ...
- 【目录】sql server 进阶篇系列
随笔分类 - sql server 进阶篇系列 sql server 下载安装标记 摘要: SQL Server 2017 的各版本和支持的功能 https://docs.microsoft.com/ ...
- 第1/24周 SQL Server 如何执行一个查询
大家好,欢迎来到第1周的SQL Server性能调优培训.在我们进入SQL Server性能调优里枯燥难懂的细节内容之前,我想通过讲解SQL Server如何执行一个查询来建立基础.这个部分非常重要, ...
- 在SQL Server 2016里使用查询存储进行性能调优
作为一个DBA,排除SQL Server问题是我们的职责之一,每个月都有很多人给我们带来各种不能解释却要解决的性能问题. 我就多次听到,以前的SQL Server的性能问题都还好且在正常范围内,但现在 ...
- Sql Server来龙去脉系列之三 查询过程跟踪
我们在读写数据库文件时,当文件被读.写或者出现错误时,这些过程活动都会触发一些运行时事件.从一个用户角度来看,有些时候会关注这些事件,特别是我们调试.审核.服务维护.例如,当数据库错误出现.列数据被更 ...
- SQL Server 锁表、查询被锁表、解锁相关语句
SQL Server 锁表.查询被锁表.解锁相关语句,供参考. --锁表(其它事务不能读.更新.删除) BEGIN TRAN SELECT * FROM <表名> WITH(TABLOCK ...
随机推荐
- 【BZOJ4891】[TJOI2017]龙舟(Pollard_rho)
[BZOJ4891][TJOI2017]龙舟(Pollard_rho) 题面 BZOJ 洛谷 题解 看了半天题....就是让你求\(\frac{b}{a}\)在模\(M\)意义下的值... 首先把\( ...
- Luogu P4774 / LOJ2721 【[NOI2018]屠龙勇士】
真是个简单坑题...++ 前置: exgcd,exCRT,STL-multiset 读完题不难发现,攻击每条龙用的剑都是可以确定的,可以用multiset求.攻击最少显然应该对于每一条龙都操作一次,即 ...
- [NOI2005]聪聪与可可(期望dp)
题意:给一张无向图,有一只猫和一只老鼠,猫每秒会向老鼠的方向移动两个单位,若它们的距离为一,那么只会移动一个单位,老鼠会等概率向周围移动一步或不动,求猫抓到老鼠的期望时间. Solutionluogu ...
- 为什么每次app访问服务器都建立新连接 导致服务器大量连接疯涨
运维发现服务器有大量连接不释放,而且每次app访问都会建立新连接. netstat -antlp |grep ESTAB|grep 8080|wc -l (访问服务器8080端口的已建立的连接数 ...
- codeforces Hello 2019(未写完)
A. Gennady and a Card Game a题惯例签到题 题意:给你一张牌,再给你5张牌,判断能不能出一次牌... 所以只要检查第二行中的某个卡是否与第一行中的卡具有共同字符 有就输出YE ...
- 洛谷P4175 网络管理
题意:链上带修第k大. 这毒瘤题...别看题意只有7个字,能把我吊打死... 介绍其中两种做法好了.其实思想上是一样的. 对于每一个点,建立权值线段树,维护它到根路径上的所有权值. 一条路径上的点集就 ...
- linux 系统调用之文件操作
fcntl 文件控制 open 打开文件 creat 创建新文件 close 关闭文件描述字 read 读文件 write 写文件 readv 从文件读入数据到缓冲数组中 writev 将缓冲数组里的 ...
- 使用postman测试dubbo服务层的方法
下面的项目用的是servlet3.0架构 接口(doubbo消费者项目和服务者项目共享的jar项目中): package serviceinvoke; import com.alibaba.dubbo ...
- pytest 1.简单介绍一,安装和如何运行
一.pytest是一个接口测试框架,试用版起来比较轻便灵活.首先来介绍他的安装: 直接使用命令 : pip install -U pytest 通过命令 :pytest --version 来查看版 ...
- Failed to resolve: com.android.support:appcompat-v7:28 问题解决
apply plugin: 'com.android.application' android { compileSdkVersion buildToolsVersion "28.0.2&q ...