SQL中WITH AS的使用實(shí)現(xiàn)
一.WITH AS的含義
WITH AS短語(yǔ),也叫做子查詢部分(subquery factoring),可以定義一個(gè)SQL片斷,該SQL片斷會(huì)被整個(gè)SQL語(yǔ)句用到??梢允筍QL語(yǔ)句的可讀性更高,也可以在UNION ALL的不同部分,作為提供數(shù)據(jù)的部分。
對(duì)于UNION ALL,使用WITH AS定義了一個(gè)UNION ALL語(yǔ)句,當(dāng)該片斷被調(diào)用2次以上,優(yōu)化器會(huì)自動(dòng)將該WITH AS短語(yǔ)所獲取的數(shù)據(jù)放入一個(gè)Temp表中。而提示meterialize則是強(qiáng)制將WITH AS短語(yǔ)的數(shù)據(jù)放入一個(gè)全局臨時(shí)表中。很多查詢通過(guò)該方式都可以提高速度。
二.使用方法
先看下面一個(gè)嵌套的查詢語(yǔ)句:
select * from person.StateProvince where CountryRegionCode in (select CountryRegionCode from person.CountryRegion where Name like 'C%' )
上面的查詢語(yǔ)句使用了一個(gè)子查詢。雖然這條SQL語(yǔ)句并不復(fù)雜,但如果嵌套的層次過(guò)多,會(huì)使SQL語(yǔ)句非常難以閱讀和維護(hù)。因此,也可以使用表變量的方式來(lái)解決這個(gè)問(wèn)題,SQL語(yǔ)句如下:
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語(yǔ)句要比第一種方式更復(fù)雜,但卻將子查詢放在了表變量@t中,這樣做將使SQL語(yǔ)句更容易維護(hù),但又會(huì)帶來(lái)另一個(gè)問(wèn)題,就是性能的損失。
由于表變量實(shí)際上使用了臨時(shí)表,從而增加了額外的I/O開銷,因此,表變量的方式并不太適合數(shù)據(jù)量大且頻繁查詢的情況。
為此,在SQL Server 2005中提供了另外一種解決方案,這就是公用表表達(dá)式(CTE),使用CTE,可以使SQL語(yǔ)句的可維護(hù)性,同時(shí),CTE要比表變量的效率高得多。
下面是CTE的語(yǔ)法:
[ WITH <common_table_expression> [ ,n ] ] <common_table_expression>::= expression_name [ ( column_name [ ,n ] ) ] AS ( CTE_query_definition )
現(xiàn)在使用CTE來(lái)解決上面的問(wèn)題,SQL語(yǔ)句如下:
with cte as ( select CountryRegionCode from person.CountryRegion where Name like 'C%' ) select * from person.StateProvince where CountryRegionCode in (select * from cte)
其中cte是一個(gè)公用表表達(dá)式,該表達(dá)式在使用上與表變量類似,只是SQL Server 2005在處理公用表表達(dá)式的方式上有所不同。
三. 語(yǔ)法注意
1、CTE后面必須直接跟使用CTE的SQL語(yǔ)句(如select、insert、update等),否則,CTE將失效。如下面的SQL語(yǔ)句將無(wú)法正常使用CTE:
with cte as ( select CountryRegionCode from person.CountryRegion where Name like 'C%' ) -- 加上這句會(huì)報(bào)錯(cuò),應(yīng)將這條SQL語(yǔ)句去掉 select * from person.CountryRegion -- 使用CTE的SQL語(yǔ)句應(yīng)緊跟在相關(guān)的CTE后面 -- select * from person.StateProvince where CountryRegionCode in (select * from cte)
2、CTE后面也可以跟其他的CTE,但只能使用一個(gè)with,多個(gè)CTE中間用逗號(hào)(,)分隔,如下面的SQL語(yǔ)句所示:
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的表達(dá)式名稱與某個(gè)數(shù)據(jù)表或視圖重名,則緊跟在該CTE后面的SQL語(yǔ)句使用的仍然是CTE
當(dāng)然,后面的SQL語(yǔ)句使用的就是數(shù)據(jù)表或視圖了,如下面的SQL語(yǔ)句所示:
-- table1是一個(gè)實(shí)際存在的表
with table1 as
(
select * from persons where age < 30
)
select * from table1 -- 使用了名為table1的公共表表達(dá)式
select * from table1 -- 使用了名為table1的數(shù)據(jù)表
4、CTE 可以引用自身
也可以引用在同一 WITH 子句中預(yù)先定義的 CTE。不允許前向引用。
--使用遞歸公用表表達(dá)式顯示遞歸的多個(gè)級(jí)別
WITH DirectReports(ManagerID, EmployeeID, EmployeeLevel) AS
(
SELECT ManagerID, EmployeeID, 0 AS EmployeeLevel
FROM HumanResources.Employee
WHERE ManagerID IS NULL
UNION ALL
SELECT e.ManagerID, e.EmployeeID, EmployeeLevel + 1
FROM HumanResources.Employee e
INNER JOIN DirectReports d
ON e.ManagerID = d.EmployeeID
)
SELECT ManagerID, EmployeeID, EmployeeLevel
FROM DirectReports ;
--使用遞歸公用表表達(dá)式顯示遞歸的兩個(gè)級(jí)別
WITH DirectReports(ManagerID, EmployeeID, EmployeeLevel) AS
(
SELECT ManagerID, EmployeeID, 0 AS EmployeeLevel
FROM HumanResources.Employee
WHERE ManagerID IS NULL
UNION ALL
SELECT e.ManagerID, e.EmployeeID, EmployeeLevel + 1
FROM HumanResources.Employee e
INNER JOIN DirectReports d
ON e.ManagerID = d.EmployeeID
)
SELECT ManagerID, EmployeeID, EmployeeLevel
FROM DirectReports
WHERE EmployeeLevel <= 2
--使用遞歸公用表表達(dá)式顯示層次列表
WITH DirectReports(Name, Title, EmployeeID, EmployeeLevel, Sort)
AS (SELECT CONVERT(varchar(255), c.FirstName + ' ' + c.LastName),
e.Title,
e.EmployeeID,
1,
CONVERT(varchar(255), c.FirstName + ' ' + c.LastName)
FROM HumanResources.Employee AS e
JOIN Person.Contact AS c ON e.ContactID = c.ContactID
WHERE e.ManagerID IS NULL
UNION ALL
SELECT CONVERT(varchar(255), REPLICATE ('| ' , EmployeeLevel) +
c.FirstName + ' ' + c.LastName),
e.Title,
e.EmployeeID,
EmployeeLevel + 1,
CONVERT (varchar(255), RTRIM(Sort) + '| ' + FirstName + ' ' +
LastName)
FROM HumanResources.Employee as e
JOIN Person.Contact AS c ON e.ContactID = c.ContactID
JOIN DirectReports AS d ON e.ManagerID = d.EmployeeID
)
SELECT EmployeeID, Name, Title, EmployeeLevel
FROM DirectReports
ORDER BY Sort
--使用 MAXRECURSION 取消一條語(yǔ)句
--可以使用 MAXRECURSION 來(lái)防止不合理的遞歸 CTE 進(jìn)入無(wú)限循環(huán)。以下示例特意創(chuàng)建了一個(gè)無(wú)限循環(huán),然后使用 MAXRECURSION 提示將遞歸級(jí)別限制為兩級(jí)
WITH cte (EmployeeID, ManagerID, Title) as
(
SELECT EmployeeID, ManagerID, Title
FROM HumanResources.Employee
WHERE ManagerID IS NOT NULL
UNION ALL
SELECT cte.EmployeeID, cte.ManagerID, cte.Title
FROM cte
JOIN HumanResources.Employee AS e
ON cte.ManagerID = e.EmployeeID
)
--Uses MAXRECURSION to limit the recursive levels to 2
SELECT EmployeeID, ManagerID, Title
FROM cte
OPTION (MAXRECURSION 2)
--在更正代碼錯(cuò)誤之后,就不再需要 MAXRECURSION。以下示例顯示了更正后的代碼
WITH cte (EmployeeID, ManagerID, Title)
AS
(
SELECT EmployeeID, ManagerID, Title
FROM HumanResources.Employee
WHERE ManagerID IS NOT NULL
UNION ALL
SELECT e.EmployeeID, e.ManagerID, e.Title
FROM HumanResources.Employee AS e
JOIN cte ON e.ManagerID = cte.EmployeeID
)
SELECT EmployeeID, ManagerID, Title
FROM cte
5、不能在 CTE_query_definition 中使用以下子句:
(1)COMPUTE 或 COMPUTE BY
(2)ORDER BY(除非指定了 TOP 子句)
(3)INTO
(4)帶有查詢提示的 OPTION 子句
(5)FOR XML
(6)FOR BROWSE
如果將 CTE 用在屬于批處理的一部分的語(yǔ)句中,那么在它之前的語(yǔ)句必須以分號(hào)結(jié)尾,如下面的SQL所示:
declare @s nvarchar(3) set @s = 'C%' ; -- 必須加分號(hào) with t_tree as ( select CountryRegionCode from person.CountryRegion where Name like @s ) select * from person.StateProvince where CountryRegionCode in (select * from t_tree)
到此這篇關(guān)于SQL中WITH AS的使用實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)SQL WITH AS內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SQL Server高級(jí)內(nèi)容之子查詢和表鏈接概述及使用
子查詢就是在查詢的where子句中的判斷依據(jù)是另一個(gè)查詢的結(jié)果,表鏈接就是將多個(gè)表合成為一個(gè)表,但是不是向union一樣做結(jié)果集的合并操作,但是表鏈接可以將不同的表合并,并且共享字段,感興趣的你可以了解下本文2013-03-03
SQL查詢連續(xù)登陸7天以上的用戶的方法實(shí)現(xiàn)
本文主要介紹了SQL查詢連續(xù)登陸7天以上的用戶的方法實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-12-12
DATASET 與 DATAREADER對(duì)象有什么區(qū)別
DataReader和DataSet最大的區(qū)別在于,DataReader使用時(shí)始終占用SqlConnection(俗稱:非斷開式連接),在線操作數(shù)據(jù)庫(kù)時(shí),任何對(duì)SqlConnection的操作都會(huì)引發(fā)DataReader的異常。下面同本文對(duì)dataset與datareader的區(qū)別詳細(xì)學(xué)習(xí)吧2016-11-11
一個(gè)完整的SQL SERVER數(shù)據(jù)庫(kù)全文索引的示例介紹
以下是介紹SQL SERVER數(shù)據(jù)庫(kù)全文索引的示例,以pubs數(shù)據(jù)庫(kù)為例。需要的朋友參考下2013-07-07
SQL SERVER數(shù)據(jù)庫(kù)表記錄只保留N天圖文教程
本篇向大家介紹SQL Server 2008 R2數(shù)據(jù)庫(kù)中數(shù)據(jù)表保留10天記錄,需要的朋友可以參考下2015-09-09
利用 SQL Server 過(guò)濾索引提高查詢語(yǔ)句的性能分析
本文就給大家介紹一下 Microsoft SQL Server 中的過(guò)濾索引功能,本文通過(guò)場(chǎng)景模擬分析給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧2021-07-07
談?wù)剆qlserver自定義函數(shù)與存儲(chǔ)過(guò)程的區(qū)別
這篇文章主要介紹了談?wù)剆qlserver自定義函數(shù)與存儲(chǔ)過(guò)程的區(qū)別,需要的朋友可以參考下2014-09-09

