程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 數據庫知識 >> 其他數據庫知識 >> MSSQL >> 關於SQL中CTE(公用表表達式)(Common Table Expression)的總結

關於SQL中CTE(公用表表達式)(Common Table Expression)的總結

編輯:MSSQL

關於SQL中CTE(公用表表達式)(Common Table Expression)的總結。本站提示廣大學習愛好者:(關於SQL中CTE(公用表表達式)(Common Table Expression)的總結)文章只能為提供參考,不一定能成為您想要的結果。以下是關於SQL中CTE(公用表表達式)(Common Table Expression)的總結正文


一.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)

CTE除可以簡化嵌套SQL語句外,還可以停止遞歸挪用。

微軟從SQl2005起引入了CTE(Common Table Expression)以強化T-SQL。這是一個相似於非耐久視圖的好東東。

依照MSDN引見

1、公用表表達式 (CTE) 可以以為是在單個 SELECT、INSERT、UPDATE、DELETE 或 CREATE VIEW 語句的履行規模內界說的暫時成果集。CTE 與派生表相似,詳細表示在不存儲為對象,而且只在查詢時代有用。與派生表的分歧的地方在於,CTE 可自援用,還可在統一查詢中援用屢次。
CTE 可用於:
創立遞歸查詢。有關具體信息,請參閱應用公用表表達式的遞歸查詢。

在不須要慣例應用視圖時調換視圖,也就是說,不用將界說存儲在元數據中。

啟用按從標量嵌套 select 語句派生的列停止分組,或許按不肯定性函數或有內部拜訪的函數停止分組。


在統一語句中屢次援用生成的表。

應用 CTE 可以取得進步可讀性和輕松保護龐雜查詢的長處。查詢可以分為零丁塊、簡略塊、邏輯生成塊。以後,這些簡略塊可用於生成更龐雜的暫時 CTE,直到生成終究成果集。可以在用戶界說的例程(如函數、存儲進程、觸發器或視圖)中界說 CTE。
2、公用表表達式 (CTE) 具有一個主要的長處,那就是可以或許援用其本身,從而創立遞歸 CTE。遞歸 CTE 是一個反復履行初始 CTE 以前往數據子集直到獲得完全成果集的公用表表達式。當某個查詢援用遞歸 CTE 時,它即被稱為遞歸查詢。遞歸查詢平日用於前往分層數據,例如:顯示某個組織圖中的雇員或物料清雙方案(個中父級產物有一個或多個組件,而那些組件能夠還有子組件,或許是其他父級產物的組件)中的數據。
遞 歸 CTE 可以極年夜地簡化在 SELECT、INSERT、UPDATE、DELETE 或 CREATE VIEW 語句中運轉遞歸查詢所需的代碼。在 SQL Server 的晚期版本中,遞歸查詢平日須要應用暫時表、游標和邏輯來掌握遞歸步調流。有關公用表表達式的具體信息,請參閱應用公用表表達式。

這裡舉例解釋以下:

為了描寫便利,邀月專程羅列了一個罕見的自聯系關系Table
表構造以下:
表構造

CREATE TABLE [dbo].[CategorySelf](
[PKID] [int] IDENTITY(1,1) NOT NULL,
[C_Name] [nvarchar](50) NOT NULL,
[C_Level] [int] NOT NULL,
[C_Code] [nvarchar](255) NULL,
[C_Parent] [int] NOT NULL,
[InsertTime] [datetime] NOT NULL,
[InsertUser] [nvarchar](50) NULL,
[UpdateTime] [datetime] NOT NULL,
[UpdateUser] [nvarchar](50) NULL,
[SortLevel] [int] NOT NULL,
[CurrState] [smallint] NOT NULL,
[F1] [int] NOT NULL,
[F2] [nvarchar](255) NULL

CONSTRAINT [PK_OBJECTCATEGORYSELF] PRIMARY KEY CLUSTERED
(
[PKID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

GO

再拔出一些測試數據

Insert
INSERT INTO [CategorySelf]([C_Name],[C_Level] ,[C_Code],[C_Parent] ,[InsertTime] ,[InsertUser] ,[UpdateTime] ,[UpdateUser] ,[SortLevel] ,[CurrState] ,[F1] ,[F2])
select '分類1',1,'0',0,GETDATE(),'testUser',DATEADD(dd,1,getdate()),'CrackUser',13,0,1,'邀月備注' union all
select '分類2',1,'0',0,GETDATE(),'testUser',DATEADD(dd,78,getdate()),'CrackUser',12,0,1,'邀月備注' union all
select '分類3',1,'0',0,GETDATE(),'testUser',DATEADD(dd,6,getdate()),'CrackUser',10,0,1,'邀月備注' union all
select '分類4',2,'1',1,GETDATE(),'testUser',DATEADD(dd,75,getdate()),'CrackUser',19,0,1,'邀月備注' union all
select '分類5',2,'2',2,GETDATE(),'testUser',DATEADD(dd,3,getdate()),'CrackUser',17,0,1,'邀月備注' union all
select '分類6',3,'1/4',4,GETDATE(),'testUser',DATEADD(dd,4,getdate()),'CrackUser',16,0,1,'邀月備注' union all
select '分類7',3,'1/4',4,GETDATE(),'testUser',DATEADD(dd,5,getdate()),'CrackUser',4,0,1,'邀月備注' union all
select '分類8',3,'2/5',5,GETDATE(),'testUser',DATEADD(dd,6,getdate()),'CrackUser',3,0,1,'邀月備注' union all
select '分類9',4,'1/4/6',6,GETDATE(),'testUser',DATEADD(dd,7,getdate()),'CrackUser',5,0,1,'邀月備注' union all
select '分類10',4,'1/4/6',6,GETDATE(),'testUser',DATEADD(dd,7,getdate()),'CrackUser',63,0,1,'邀月備注' union all
select '分類11',4,'1/4/6',6,GETDATE(),'testUser',DATEADD(dd,8,getdate()),'CrackUser',83,0,1,'邀月備注' union all
select '分類12',4,'2/5/8',8,GETDATE(),'testUser',DATEADD(dd,10,getdate()),'CrackUser',3,0,1,'邀月備注' union all
select '分類13',4,'2/5/8',8,GETDATE(),'testUser',DATEADD(dd,15,getdate()),'CrackUser',1,0,1,'邀月備注'

一個典范的運用場景是:在這個自聯系關系的表中,查詢以PKID為2的分類包括一切子分類。或許許多情形下,我們不能不用暫時表/表變量/游標等。如今我們有了CTE,就簡略多了

CTEDemo1
WITH SimpleRecursive(C_Name, PKID, C_Code,C_Parent)
AS
(SELECT C_Name, PKID, C_Code,C_Parent FROM CategorySelf WHERE PKID = 2
UNION ALL
SELECT p.C_Name, p.PKID, p.C_Code,p.C_parent
FROM CategorySelf P INNER JOIN
SimpleRecursive A ON A.PKID = P.C_Parent
)
SELECT sr.C_Name as C_Name, c.C_Name as C_ParentName,sr.C_Code as C_ParentCode
FROM SimpleRecursive sr inner join CategorySelf c
on sr.C_Parent=c.PKID

查詢成果以下:C_Name C_ParentName C_ParentCode
分類5 分類2 2
分類8 分類5 2/5
分類12 分類8 2/5/8
分類13 分類8 2/5/8
感到怎樣樣?假如我只想查詢第二層,而不是默許的無窮查詢下去,
可以在下面的SQL後加一個選項 Option(MAXRECURSION 5),留意5表現到第5層就不往下找了。假如只想找第二層,但現實成果有三層,此時會失足,
Msg 530, Level 16, State 1, Line 1
The statement terminated. The maximum recursion 1 has been exhausted before statement completion.
此時可以經由過程where前提來處理,而包管不失足,看以下SQL語句:
CTEDemo2

WITH SimpleRecursive(C_Name, PKID, C_Code,C_Parent,Sublevel)
AS
(SELECT C_Name, PKID, C_Code,C_Parent,0 FROM CategorySelf WHERE PKID = 2
UNION ALL
SELECT p.C_Name, p.PKID, p.C_Code,p.C_parent,Sublevel+1
FROM CategorySelf P INNER JOIN
SimpleRecursive A ON A.PKID = P.C_Parent
)
SELECT sr.C_Name as C_Name, c.C_Name as C_ParentName,sr.C_Code as C_ParentCode
FROM SimpleRecursive sr inner join CategorySelf c
on sr.C_Parent=c.PKID
where SubLevel<=2

查詢成果:
C_Name C_ParentName C_ParentCode
分類5 分類2 2
分類8 分類5 2/5
固然,我們不是說CTE就是全能的。經由過程好的表設計也能夠某種水平上處理特定的成績。上面用慣例的SQL完成下面這個需求。
留意:下面表中有一個字段很主要,就是C_Code,編碼 ,格局如"1/2",“2/5/8"表現該分類的下級分類是1/2,2/5/8
如許,我們查詢就簡略多,查詢以PKID為2的分類包括一切子分類:

SELECT C_Name as C_Name, (Select top 1 C_Name from CategorySelf s where c.C_Parent=s.PKID) as C_ParentName,C_Code as C_ParentCode
from CategorySelf c where C_Code like '2/%'

查詢以PKID為2的分類包括一切子分類,且級別不年夜於3

SELECT C_Name as C_Name, (Select top 1 C_Name from CategorySelf s where c.C_Parent=s.PKID) as C_ParentName,C_Code as C_ParentCode
from CategorySelf c where C_Code like '2/%' and C_Level<=3

查詢成果同上,略去。這裡我們看出,有時刻,好的表構造設計相當主要。
邀月於2009.10.23 1:36 完成份享。
有人很關懷機能成績。今朝沒有測試過。稍後會附上百萬級測試申報。不外,有兩點懂得邀月忘了彌補:
1、CTE實際上是面向對象的,運轉的基本是CLR。一個很好的解釋是With查詢語句中是辨別字段的年夜小寫的。即"C_Code"和"c_Code"是紛歧樣的,後者會報錯。這與通俗的SQL語句分歧。
2、 這個運用示例重在簡化營業邏輯,即使是機能欠安,但對暫時表/表變量/游標等傳統處置方法是一種營業條理上的簡化或許說是優化。

公用表表達式 (CTE) 可以以為是在單個 SELECT、INSERT、UPDATE、DELETE 或 CREATE VIEW 語句的履行規模內界說的暫時成果集。CTE 與派生表相似,詳細表示在不存儲為對象,而且只在查詢時代有用。與派生表的分歧的地方在於,CTE 可自援用,還可在統一查詢中援用屢次。
CTE 可用於:
創立遞歸查詢。
在不須要慣例應用視圖時調換視圖,也就是說,不用將界說存儲在元數據中。
啟用按從標量嵌套 select 語句派生的列停止分組,或許按不肯定性函數或有內部拜訪的函數停止分組。
在統一語句中屢次援用生成的表。
應用 CTE 可以取得進步可讀性和輕松保護龐雜查詢的長處。查詢可以分為零丁塊、簡略塊、邏輯生成塊。以後,這些簡略塊可用於生成更龐雜的暫時 CTE,直到生成終究成果集。
可以在用戶界說的例程(如函數、存儲進程、觸發器或視圖)中界說 CTE。
CTE 的構造
CTE 由表現 CTE 的表達式稱號、可選列列表和界說 CTE 的查詢構成。界說 CTE 後,可以在 SELECT、INSERT、UPDATE 或 DELETE 語句中對其停止援用,就像援用表或視圖一樣。CTE 也可用於 CREATE VIEW 語句,作為界說 SELECT 語句的一部門。
CTE 的根本語法構造以下:
WITH expression_name [ ( column_name [,...n] ) ]
AS
( CTE_query_definition )
只要在查詢界說中為一切成果列都供給了分歧的稱號時,列稱號列表才是可選的。
運轉 CTE 的語句為:
SELECT <column_list>
FROM expression_name
示例:

with s_name as
(
select s.name, sc.c,sc.grade from SQL Server student AS s,sc
where s.s#=sc.s#
)
select * from s_name

在應用CTE時應留意以下幾點:
1. CTE前面必需直接跟應用CTE的SQL語句(如select、insert、update等),不然,CTE將掉效。
2. CTE前面也能夠跟其他的CTE,但只能應用一個with,多個CTE中央用逗號(,)分隔。
3. 假如CTE的表達式稱號與某個數據表或視圖重名,則緊跟在該CTE前面的SQL語句應用的依然是CTE,固然,前面的SQL語句應用的就是數據表或視圖了。
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 用在屬於批處置的一部門的語句中,那末在它之前的語句必需以分號開頭。
  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved