?今天偶爾看到sql中也有with關鍵字,好歹也寫了幾年的sql語句,居然第一次接觸,無知啊。看了一位博主的文章,自己添加了一些內容,做了簡單的總結,這個語句還是第一次見到,學習了。我從簡單到復雜地寫,希望高手們不要見笑。下面的sql語句設計到三個表,表的內容我用txt文件復制進去,這里不妨使用上一個隨筆介紹的建立端到端的package的方法將這些表導入到數據庫中,具體的就不說了。
從這里下載文件 employees.txt,customers.txt,orders.txt
參考文章: http://www.cnblogs.com/wwan/archive/2011/02/24/1964279.html
使用package導入數據: http://www.cnblogs.com/tylerdonet/archive/2011/04/17/2017471.html
簡單的聚合
從orders表中選擇各個年份共有共有多少客戶訂購了商品
-
?第一種寫法,我們可以寫成這樣
要注意的是如果把group by YEAR(o.orderdata)換成group by orderyear就會出錯,這里涉及到sql語句的執行順序問題,有時間再了解一下1 select YEAR (o.orderdate) orderyear, COUNT ( distinct (custid)) numCusts
2 from Sales.Orders o
3 group by YEAR (o.orderdate)
4 go -
第二種寫法,
在from語句中先得到orderyear,然后再select語句中就不會出現沒有這個字段的錯誤了1 select orderyear, COUNT ( distinct (custid))numCusts
2 from ( select YEAR (orderdate) as orderyear,custid from sales.orders) as D
3 group by orderyear
4 go -
第三種寫法,
在as D后面加上選擇出的字段,是不是更加的清楚明了呢!1 select orderyear, COUNT ( distinct (custid)) numCusts
2 from ( select YEAR (orderdate),custid from sales.orders) as D(orderyear,custid)
3 group by orderyear
4 go -
第四種寫法,with出場了
with可以使語句更加的經湊,下面是權威解釋。1 with c as (
2 select YEAR (orderdate) orderyear, custid from sales.orders)
3 select orderyear, COUNT ( distinct (custid)) numCusts from c group by orderyear
4 go
指定臨時命名的結果集,這些結果集稱為公用表表達式 (CTE)。該表達式源自簡單查詢,并且在單條 SELECT、INSERT、UPDATE、MERGE 或 DELETE 語句的執行范圍內定義。該子句也可用在 CREATE VIEW 語句中,作為該語句的 SELECT 定義語句的一部分。公用表表達式可以包括對自身的引用。這種表達式稱為遞歸公用表達式。
----MSDN
-
第五種寫法,也可以借鑒第三種寫法,這樣使語句更加清楚明了,便于維護
上面5中寫法都得到相同的結果,如下圖1:1 with c(orderyear,custid) as (
2 ? select YEAR (orderdate),custid from sales.orders)
3 ? select orderyear, COUNT ( distinct (custid)) numCusts from c group by c.orderyear
4 ? go
圖1
添加計算
-
現在要求要求計算出訂單表中每年比上一年增加的客戶數目,這個稍微復雜
這里兩次使用到with結果集。查詢得到的結果如下圖21 with yearcount as (
2 ? select YEAR (orderdate) orderyear, COUNT ( distinct (custid)) numCusts from sales.orders group by YEAR (orderdate))
3 ? select cur.orderyear curyear,cur.numCusts curNumCusts,prv.orderyear prvyear,prv.numCusts prvNumCusts,cur.numCusts - prv.numCusts growth
4 ? from yearcount cur left join yearcount prv on cur.orderyear = prv.orderyear + 1
5 go
圖2
復雜的計算
-
查找客戶id,這些客戶和所有來自美國的雇員至少有一筆交易記錄,查詢語句如下
這里嵌套with語句,第with語句查找美國雇員的id,第二個語句使用這個結果和擁有客戶的客戶id和擁有關系標識做笛卡爾積運算。最后從這個笛卡爾積中通過標識找到最終的custid。1 with TheseEmployees as (
2 select empid from hr.employees where country = ' USA ' ),
3 CharacteristicFunctions as (
4 select custid,
5 case when custid in ( select custid from sales.orders as o where o.empid = e.empid) then 1 else 0 end as charfun
6 from sales.customers as c cross join TheseEmployees as e)
7 select custid, min (charfun) from CharacteristicFunctions group by custid having min (charfun) = 1
8 go
結果如下圖3
圖3
這里只有簡單地介紹,沒有深入,高手們不要見笑啊。
---------------------------------------------------------分界線----------------------------------------------------------
with語句和子查詢的性能比較
在博友 SingleCat 的提醒下,對 with 語句做一些性能測試,這里使用的測試工具是 SQL Server Profile 。我選擇了最后一個語句,因為這個語句比較復雜一點。開始的時候單獨執行一次發現他們的差別不大,就差幾個毫秒,后來想讓他們多執行幾次,連續執行 10
次看看執行的結果。下面貼出測試用的語句。
1
/*
with查詢
*/
2
declare
@withquery
varchar
(
5000
)
3
declare
@execcount
int
=
0
4
set
@withquery
=
'
with TheseEmployees as(
5
select empid from hr.employees where country=N
''
USA
''
),
6
CharacteristicFunctions as(
7
select custid,
8
case when custid in (select custid from sales.orders as o where o.empid=e.empid) then 1 else 0 end as charfun
9
from sales.customers as c cross join TheseEmployees as e)
10
select custid from CharacteristicFunctions group by custid having min(charfun)=1 order by custid
11
'
12
while
@execcount
<
10
13
begin
14
exec
(
@withquery
);
15
set
@execcount
=
@execcount
+
1
16
end
17
18
/*
子查詢
*/
19
declare
@subquery
varchar
(
5000
)
20
declare
@execcount
int
=
0
21
set
@subquery
=
'
select custid from Sales.Orders where empid in
22
(select empid from HR.Employees where country = N
''
USA
''
) group by custid
23
having count(distinct empid)=(select count(*) from HR.Employees where country = N
''
USA
''
);
24
'
25
while
@execcount
<
10
26
begin
27
exec
(
@subquery
);
28
set
@execcount
=
@execcount
+
1
29
end
從 SQL Server Profile 中截圖如下
從圖中可以看到子查詢語句的執行時間要少于with語句,我覺得主要是with查詢中有一個cross join做了笛卡爾積的關系,于是又實驗了上面的那個簡單一點的,下面是測試語句。
1
/*
with語句
*/
2
declare
@withquery
varchar
(
5000
)
3
declare
@execcount
int
=
0
4
set
@withquery
=
'
with c(orderyear,custid) as(
5
select YEAR(orderdate),custid from sales.orders)
6
select orderyear,COUNT(distinct(custid)) numCusts from c group by c.orderyear
'
7
while
@execcount
<
100
8
begin
9
exec
(
@withquery
);
10
set
@execcount
=
@execcount
+
1
11
end
12
13
/*
子查詢
*/
14
declare
@subquery
varchar
(
5000
)
15
declare
@execcount
int
=
0
16
set
@subquery
=
'
select orderyear,COUNT(distinct(custid)) numCusts
17
from (select YEAR(orderdate),custid from sales.orders) as D(orderyear,custid)
18
group by orderyear
'
19
while
@execcount
<
100
20
begin
21
exec
(
@subquery
);
22
set
@execcount
=
@execcount
+
1
23
end
?
這次做10次查詢還是沒有多大的差距,with語句用10個duration,子查詢用了11個,有時候還會翻過來。于是把執行次數改成100,這次還是子查詢使用的時間要少,截圖如下
最終結論,子查詢好比with語句效率高。
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061
微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元

