使用WITH AS提高性能簡化嵌套SQL 一.WITH AS的含義 WITH AS短語,也叫做子查詢部分(subquery factoring),可以讓你做很多事情,定義一個SQL片斷,該SQL片斷會 被整個SQL語句所用到。有的時候,是為了讓SQL語句的可讀性更高些,也有可能是在UNION ALL ...
使用
WITH
AS
提高性能簡化嵌套SQL
一.
WITH
AS
的含義
WITH
AS
短語,也叫做子查詢部分(subquery factoring),可以讓你做很多事情,定義一個SQL片斷,該SQL片斷會
被整個SQL語句所用到。有的時候,是為了讓SQL語句的可讀性更高些,也有可能是在
UNION
ALL
的不同部分,作為提供數
據的部分。
特別對於
UNION
ALL
比較有用。因為
UNION
ALL
的每個部分可能相同,但是如果每個部分都去執行一遍的話,則成本太高,
所以可以使用
WITH
AS
短語,則只要執行一遍即可。如果
WITH
AS
短語所定義的表名被調用兩次以上,則優化器會自動將
WITH
AS
短語所獲取的數據放入一個
TEMP
表裡,如果只是被調用一次,則不會。而提示materialize則是強制將
WITH
AS
短語里的數據放入一個全局臨時表裡。很多查詢通過這種方法都可以提高速度。
二.使用方法
先看下麵一個嵌套的查詢語句:
select
*
from
person.StateProvince
where
CountryRegionCode
in
(
select
CountryRegionCode
from
person.CountryRegion
where
Name
like
'C%'
) 上面的查詢語句使用了一個子查詢。雖然這條SQL語句並不複雜,但如果嵌套的層次過多,會使SQL語句非常難以閱
讀和維護。因此,也可以使用表變數的方式來解決這個問題。
SQL語句如下:
declare
@t
table
(CountryRegionCode nvarchar(3))
insert
into
@t(CountryRegionCode) (
select
CountryRegionCode
from
person.CountryRegion
where
Name
like
'C%'
)
select
*
from
person.StateProvince
where
CountryRegionCode
in
(
select
*
from
@t)
雖然上面的SQL語句要比第一種方式更複雜,但卻將子查詢放在了表變數@t中,這樣做將使SQL語句更容易維護,但又
會帶來另一個問題,就是性能的損失。由於表變數實際上使用了臨時表,從而增加了額外的I/O開銷,因此,表變數的方式
並不太適合數據量大且頻繁查詢的情況。為此,在SQL Server 2005中提供了另外一種解決方案,這就是公用表表達式(CTE),使用CTE,可以增加SQL語句的可維護性,同時,CTE要比表變數的效率高得多。
下麵是CTE的語法:
[
WITH
<common_table_expression> [ ,n ] ]
<common_table_expression>::=
expression_name [ ( column_name [ ,n ] ) ]
AS
( CTE_query_definition )
現在使用CTE來解決上面的問題,SQL語句如下:
with
cr
as
(
select
CountryRegionCode
from
person.CountryRegion
where
Name
like
'C%'
)
select
*
from
person.StateProvince
where
CountryRegionCode
in
(
select
*
from
cr)
其中cr是一個公用表表達式,該表達式在使用上與表變數類似,只是SQL Server 2005在處理公用表表達式的方式上有
所不同。
在使用CTE時應註意如下幾點:
1. CTE後面必須直接跟使用CTE的SQL語句(如
select
、
insert
、
update
等),否則,CTE將失效。如下麵的SQL語句將無法正
常使用CTE:
with
cr
as
(
select
CountryRegionCode
from
person.CountryRegion
where
Name
like
'C%'
)
select
*
from
person.CountryRegion
-- 應將這條SQL語句去掉
-- 使用CTE的SQL語句應緊跟在相關的CTE後面--
select
*
from
person.StateProvince
where
CountryRegionCode
in
(
select
*
from
cr)
2. CTE後面也可以跟其他的CTE,但只能使用一個
with
,多個CTE中間用逗號(,)分隔,如下麵的SQL語句所示:
with
cte1
as
(
select
*
from
table1
where
name
like
'abc%'
),
cte2
as
(
select
*
from
table2
where
id > 20
),
cte3
as
(
select
*
from
table3
where
price < 100
)
select
a.*
from
cte1 a, cte2 b, cte3 c
where
a.id = b.id
and
a.id = c.id
3. 如果CTE的表達式名稱與某個數據表或視圖重名,則緊跟在該CTE後面的SQL語句使用的仍然是CTE,當然,後面的SQL語句
使用的就是數據表或視圖了,如下麵的SQL語句所示:
-- table1是一個實際存在的表
with
table1
as
(
select
*
from
persons
where
age < 30
)
select
*
from
table1
-- 使用了名為table1的公共表表達式
select
*
from
table1
-- 使用了名為table1的數據表
4. CTE 可以引用自身,也可以引用在同一
WITH
子句中預先定義的CTE。不允許前向引用。
5. 不能在CTE_query_definition 中使用以下子句:
(1)COMPUTE 或COMPUTE
BY
(2)
ORDER
BY
(除非指定了
TOP
子句)
(3)
INTO
(4)帶有查詢提示的
OPTION
子句
(5)
FOR
XML
(6)
FOR
BROWSE
6. 如果將CTE 用在屬於批處理的一部分的語句中,那麼在它之前的語句必須以分號結尾,如下麵的SQL所示:
declare
@s nvarchar(3)
set
@s =
'C%'
;
-- 必須加分號
with
t_tree
as
(
select
CountryRegionCode
from
person.CountryRegion
where
Name
like
@s
)
select
*
from
person.StateProvince
where
CountryRegionCode
in
(
select
*
from
t_tree)