ASP.Net MVC_WPF_WCF_WinForm_SQL
查詢以AdventureWorks2008 資料庫為例子:
一、子查詢
1、單值Select語句巢狀查詢(巢狀在裡面的查詢只返回一個值,如:首次銷售時間)--第一天出售的所有產品ProductID select distinct soh.OrderDate,sod.ProductID from Sales.SalesOrderHeader soh join Sales.SalesOrderDetail sod on soh.SalesOrderID=sod.SalesOrderID where soh.OrderDate=(select MIN(OrderDate) from Sales.SalesOrderHeader)--獲首次銷售時間
2、多值Select語句巢狀查詢
--應聘公司另一職位的所有僱員列表
select e.BusinessEntityID,pp.FirstName,pp.LastName from HumanResources.Employee e
join Person.Person pp
on e.BusinessEntityID=pp.BusinessEntityID
where e.BusinessEntityID in
(select distinct BusinessEntityID from HumanResources.JobCandidate) --獲得求職者的ID
注意:也可以使用連表查詢實現
3、使用巢狀找到孤立資料 not in
--沒有應聘公司其他職位的僱員 select e.BusinessEntityID,pp.FirstName,pp.LastName from HumanResources.Employee e join Person.Person pp on e.BusinessEntityID=pp.BusinessEntityID where e.BusinessEntityID not in (select distinct BusinessEntityID from HumanResources.JobCandidate --獲得求職者的ID where BusinessEntityID is not null)
4、ANY、SOME和ALL運算子
二、相關子查詢
1、相關子查詢的工作方式(1)外部查詢獲得一條記錄並將其傳入內部查詢;
(2)基於傳入的值執行內部查詢;
(3)內部查詢將自己返回的結果值傳給外部查詢,外部查詢利用這些值完成自己的處理。
2、Where子句中的相關子查詢
--顧客第1次下訂單的訂單ID,訂單時間
select o1.CustomerID,o1.SalesOrderID,o1.OrderDate
from Sales.SalesOrderHeader o1
where o1.OrderDate=(select MIN(o2.OrderDate)
from Sales.SalesOrderHeader o2
where o2.CustomerID=o1.CustomerID)
order by o1.CustomerID
3、Select 列表中的相關子查詢
--查詢客戶的名字以及首次訂購日期
select pp.FirstName,pp.LastName,
(select MIN(OrderDate) from Sales.SalesOrderHeader o
where o.CustomerID=c.CustomerID) as "order date"
from Person.Person pp
join Sales.Customer c
on pp.BusinessEntityID=c.PersonID
四、派生表
--查詢購買迷你香水和棒球帽的客戶
select distinct pp.FirstName,pp.LastName from Person.Person pp
join
(select sc.PersonID from Sales.Customer sc
join Sales.SalesOrderHeader soh on sc.CustomerID=soh.CustomerID
join Sales.SalesOrderDetail sod on soh.SalesOrderID=sod.SalesOrderID
join Production.Product p on sod.ProductID=p.ProductID
where p.Name='Minipump') pumps --括號內的查詢派生表
on pp.BusinessEntityID=pumps.PersonID
join
(select sc.PersonID from Sales.Customer sc
join Sales.SalesOrderHeader soh on sc.CustomerID=soh.CustomerID
join Sales.SalesOrderDetail sod on soh.SalesOrderID=sod.SalesOrderID
join Production.Product p on sod.ProductID=p.ProductID
where p.Name='AWC Logo Cap') caps
on pp.BusinessEntityID=caps.PersonID
要記住,派生表並不能解決所有的問題。
例如:當結果相當龐大而且有大量的記錄要聯結時,可以考慮使用臨時表,並在其上構建索引(派生表沒有索引)
五、Exists 運算子
--應聘公司另一職位的僱員列表
select e.BusinessEntityID,pp.FirstName,pp.LastName from HumanResources.Employee e
join Person.Person pp
on e.BusinessEntityID=pp.BusinessEntityID
where exists --存在JobCandidate表中的員工
(select BusinessEntityID
from HumanResources.JobCandidate jc
where e.BusinessEntityID=jc.BusinessEntityID)
--資料庫是否存在
use master
go
if not exists(select 'True' from sys.databases where name='DBCreateTest')
begin
create database DBCreateTest
end
else
begin
print 'DBCreateTest資料庫已經存在'
end
go
六、Except 除外 、Intersect 交叉
set nocount on;
create table UnionTest1
(
idco1 int identity,
col2 char(3)
);
create table UnionTest2
(
idco1 int identity,
col4 char(3)
);
insert into UnionTest1
values
('AAA'),
('BBB'),
('CCC');
insert into UnionTest2
values
('CCC'),
('DDD'),
('EEE');
-- except:返回'AAA' 'BBB' 不包含'CCC'
select col2 from UnionTest1
except
select col4 from UnionTest2;
--通過not exists實現except效果
select col2 from UnionTest1 ut1
where not exists
(select col4 from UnionTest2 where col4=ut1.col2);
--intersect:返回'ccc' 為兩個結果集交叉的
select col2 from UnionTest1
intersect
select col4 from UnionTest2;
--通過exists實現intersect效果
select col2 from UnionTest1 ut1
where exists
(select col4 from UnionTest2 where col4=ut1.col2);
drop table UnionTest1;
drop table UnionTest2;
set nocount off;
注意:Except成本是 not exists 成本的兩倍
七、通用表表達式(Common Table Expression,CTE)
通過With提供一種方法按姓名引用臨時結果集,並當做表來使用
with pumps2(BusinessEntityID)
as
(select sc.PersonID from Sales.Customer sc
join Sales.SalesOrderHeader soh on sc.CustomerID=soh.CustomerID
join Sales.SalesOrderDetail sod on soh.SalesOrderID=sod.SalesOrderID
join Production.Product p on sod.ProductID=p.ProductID
where p.Name='Minipump')
select distinct pp.FirstName,pp.LastName from Person.Person as pp
join pumps2
on pp.BusinessEntityID=pumps2.BusinessEntityID;
--CTE中不能使用某些結構,其中包括
--Computer和ComputeBy
--Order By
--Into
--For Xml、For Browse和Option查詢子句
--注意:使用遞迴查詢時離不開CTE
八、遞迴查詢
1、hierarchyid資料型別學習
hierarchyid 層次關係,必須用/開始和結束
與hierarchyid有關的一些函式主要有
GetAncestor :取得某一個級別的祖先
GetDescendant :取得某一個級別的子代
GetLevel :取得級別
GetRoot :取得根
IsDescendantOf :判斷某個節點是否為某個節點的子代
Parse :將字串轉換為hierarchyid。該字串的格式通常都是/1/這樣的
Read :
Read 從傳入的 BinaryReader 讀取 SqlHierarchyId 的二進位制表示形式,並將 SqlHierarchyId 物件設定為該值。不能使用 Transact-SQL 呼叫 Read。請改為使用 CAST 或 CONVERT。
GetReparentedValue :可以用來移動節點(或者子樹)
ToString :將hierarchyid轉換為字串,與parse正好相反
Write
Write 將 SqlHierarchyId 的二進位制表示形式寫出到傳入的 BinaryWriter 中。無法通過使用 Transact-SQL 來呼叫 Write。請改為使用 CAST 或 CONVERT。
2、基礎資料準備
Employee 表結構:
生成後的Employee
create table HumanResources.Employee2
(
BusinessEntityID int not null primary key,
ManagerID int null,
JobTitle nvarchar(50) null
);
insert into HumanResources.Employee2
select hre.BusinessEntityID,
(select BusinessEntityID
from HumanResources.Employee hre2
where hre.OrganizationNode.GetAncestor(1)=hre2.OrganizationNode
),
JobTitle
from HumanResources.Employee hre;
select * from HumanResources.Employee2
3、建立CTE,遞推查詢
--我們可以使用一個遞迴查詢來沿著這條鏈前進。
--with 為遞迴建立CTE
with Reports(ManagerID,BusinessEntityID,JobTitle,DepartmentID,level)
as
(
--查詢根節點(執行長)
select
hre.ManagerID,
hre.BusinessEntityID,
hre.JobTitle,
hredh.DepartmentID,
0 as level
from HumanResources.Employee2 hre
join HumanResources.EmployeeDepartmentHistory as hredh
on hre.BusinessEntityID=hredh.BusinessEntityID
and hredh.EndDate is null
where hre.ManagerID is null
union all
--向根節點彙報的那些員工,隨後沿著樹向下遞推,直至到達底部為止。
select
hre.ManagerID,
hre.BusinessEntityID,
hre.JobTitle,
hredh.DepartmentID,
r.level+1 as level
from HumanResources.Employee2 hre
join HumanResources.EmployeeDepartmentHistory as hredh
on hre.BusinessEntityID=hredh.BusinessEntityID
and hredh.EndDate is null
join Reports as r
on hre.ManagerID=r.BusinessEntityID
)
select ManagerID,BusinessEntityID,JobTitle,level
from Reports r
join HumanResources.Department as dp
on r.DepartmentID=dp.DepartmentID
where dp.GroupName like '%Admin%'
order by level,ManagerID,JobTitle
go
結果如下:
九、Merge 合併
有了Merge,我們可以在一個整體操作中併入多條DML操作語句(Insert,update和delete),改進效能(他們可以共享許多相同的物理操作)並簡化事務。Merge使用有點類似CTE的特殊Using子句。
例項:月銷售額累計
--建立月累計表(某個產品月總銷售額)
create table Sales.MonthlyRollup
(
Year smallint Not null,
Month tinyint not null,
ProductID int not null
--外來鍵 引用
Foreign key
references Production.product(productID),
QtySold int not null,
--約束 主鍵
Constraint PKYearMonthProductID
Primary Key
(Year,Month,ProductID)
);
--Merge 合併
merge Sales.MonthlyRollup as smr
using
(
select soh.OrderDate,sod.ProductID,SUM(sod.OrderQty) as QtySold from Sales.SalesOrderHeader soh
join Sales.SalesOrderDetail sod
on soh.SalesOrderID=sod.SalesOrderID
where soh.OrderDate>='2003-08-01'
and soh.OrderDate<'2003-08-02'
group by soh.OrderDate,sod.ProductID
)as s
on s.ProductID=smr.ProductID
when matched then --匹配到,只累加金額
update set smr.QtySold=smr.QtySold+s.QtySold
when not matched then--沒匹配到,插入新記錄
insert (Year,Month,ProductID,QtySold)
values(datepart(yy,s.orderdate),datepart(m,s.orderdate),s.ProductID,s.QtySold)
;
select * from Sales.MonthlyRollup
十、利用外部呼叫完成複雜操作
十一、效能考慮
使用聯結、子查詢或其他方法
1、預查詢(傳引數,儲存過程)2、在聯表前,先對記錄進行一次篩選
3、慎用派生表,執行後,會駐紮記憶體中