SQL Server进阶(五)子查询

时间:2023-03-08 22:25:36

序言

为什么需要子查询?

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

子查询

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

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

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

 SQL Server进阶(五)子查询

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:查询每个客户返回在他参与活动的最后一天下过的所有订单。

期望结果:

SQL Server进阶(五)子查询

影响行数: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;

SQL Server进阶(五)子查询

使用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);

SQL Server进阶(五)子查询

又如以下查询请求返回每个客户在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<'');

SQL Server进阶(五)子查询

SQL Server进阶(五)子查询

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 Server进阶(五)子查询

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