用到的数据库NorthWind
1:标量子查询(相对于多值子查询):
只有标量子查询返回的是单个记录或者不返回,就是有效的子查询。
Ex1:select OrderId From Orders where EmployeeId=
(select EmployeeId From employees where lastName like N'Davolio')
将’Davolio’改为'D%'时,这个时候子查询中返回结果为2行,等号右边此时为多值,查询失败.将’=’改为in谓词.查询才能通过.
employees表中无lastname=’jason’,外部查询将返回null.
2:独立子查询(相对于相关子查询)
Ex2:返回每个美国员工至少为其处理过订单的所有客户
--思路:1:Employees表中获取美国员工总数2:Orders表中查询美国员工处理的Order,对CustomerID分组后,统计其不同的EmployeeID正好等于美国员工总数
Select CustomerID From Orders Where EmployeeID In
(Select EmployeeID From Employees Where Country=N'USA')
group by CustomerID
Having Count(Distinct EmployeeID)=
(Select Count(*) From Employees Where Country=N'USA')--美国员工总数
Ex3:返回在每月最后实际订单日期发生的订单(每月最后订单日期可能不是每月最后一天)
--思路:子查询按月分组得到每月最近订单日期
Select OrderID,CustomerID,EmployeeID,OrderDate
From Orders
Where OrderDate In
(Select Max(OrderDate) From Orders Group by Convert(char(6),OrderDate,112))--112表示YYYYMMDD char(6)提取YYYYMM
3:相关子查询
引用外部查询列的子查询。逻辑上讲,子查询会为外部查询的每行计算一次。
Ex4:查询每个员工employee最近的一个订单
思路:--需要附加属性(Tiebreaker)Max(OrderDate)最大订单日期和Max(OrderId)最大订单号来确定每名员工的最近订单状况
select OrderId,CustomerId,EmployeeId,OrderDate From Orders As o1 where OrderDate=
(
select Max(OrderDate) From Orders As o2 where o2.EmployeeId=o1.EmployeeId--得到每名员工的最近订单日期
)
and OrderId=
(
select Max(OrderId) From Orders As o2 Where o2.EmployeeId=o1.EmployeeId and o2.OrderDate=o1.OrderDate--得到每名员工每个订单日期的最大OrderId
)
4:Exists()谓词
输入一个查询,是否产生行,返回True或False,不返回Unknown。查询中带*不是一个好的习惯,但Exists查询语句中可以使用。它只关心返回行,优化器忽略查询中的列表
Ex5:返回来自Spain国家且发生过订单的Customer
Select CustomerID,CompanyName From Customers AS c
Where Country=N'Spain' And
Exists(Select * From Orders AS o Where o.CustomerID=c.CustomerID )
--In版本
Select CustomerID,CompanyName From Customers AS c
Where Country=N'Spain' And
CustomerID In(Select CustomerID From Orders)
Not Exists和Not In的区别(Exists和In区别不大,产生相同执行计划)
Not In(当存在Null的时候)是不返回记录的,所以要去掉Null的情况,将和Not
Exists产生相同查询结果。但Not In 查询计划会关心列为Null的情况,所以Not Exists性能更好点
Select CustomerID,CompanyName From Customers AS c
Where Country=N'Spain' And CustomerID
NOt In(Select CustomerID From Orders where CustomerID is not Null )
5:行为不当的子查询
当子查询中有无效列的情况
首先运行
use tempdb
create table Orders
(
OrderID int not null identity(1,1),
ClientID int not null
)
Create table Customers
(
CustomerID int not null identity(1,1),
CustomerName varchar(5) not Null
)
insert into Customers (CustomerName) values('jason')
insert into Customers (CustomerName) values('mary')
insert into Orders (ClientID) values(1)
insert into Orders (ClientID) values(1)
顾客表: 订单表:
任务:返回没有定过货的顾客
先运行如下语句
Select CustomerID from Customers where
CustomerID not in(select CustomerID from Orders )—注意:CustomerID不属于OrderID
返回一个空集,不会报错。因为名称解析的时候进行从内部向外的过程,这里子查询未查询到列CustomerID,它将在Customers中查找且找到了列。为了避免这样的情况发生,好的习惯是子查询中总是为所有的列加上别名,正确的写法如下
Select CustomerID from Customers where
CustomerID not in(select o.ClientID from Orders AS o )