SQL2005/2008中的CTE應用--遞迴查詢
微軟從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
表結構如下:
<!--
Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/
-->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
再插入一些測試資料
<!--
Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/
-->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,'2/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,就簡單多了
<!--
Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/
-->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語句:
<!--
Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/
-->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的分類包含所有子分類:
Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/
-->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
Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/
-->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
完成分享。
有人很關心效能問題。目前沒有測試過。稍後會附上百萬級測試報告。不過,有兩點理解邀月忘了補充:
一、CTE其實是物件導向的,執行的基礎是CLR。一個很好的說明是With查詢語句中是區分欄位的大小寫的。即"C_Code"和"c_Code"是不一樣的,後者會報錯。這與普通的SQL語句不同。
二、 這個應用示例重在簡化業務邏輯,即便是效能不佳,但對臨時表\表變數\遊標等傳統處理方式是一種業務層次上的簡化或者說是優化。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/16436858/viewspace-617275/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- Sql Server 使用CTE實現遞迴查詢SQLServer遞迴
- 使用SQL SERVER 2005/2008 遞迴CTE查詢樹型結構SQLServer遞迴
- 透過遞迴查詢應用依賴遞迴
- oracle中的遞迴sql查詢 connect by prior實現遞迴Oracle遞迴SQL
- SQL Server中CTE的另一種遞迴方式-從底層向上遞迴SQLServer遞迴
- PostgreSQL 遞迴查詢SQL遞迴
- PostgreSQL=>遞迴查詢SQL遞迴
- SqlServer遞迴查詢SQLServer遞迴
- ORACLE 遞迴查詢Oracle遞迴
- sqlalchemy 遞迴查詢SQL遞迴
- Oracle SQL的遞迴查詢OracleSQL遞迴
- SQL Server遞迴查詢SQLServer遞迴
- Oracle遞迴查詢sqlOracle遞迴SQL
- 一個遞迴查詢遞迴
- 遞迴查詢子元素遞迴
- MS SQL Server的遞迴查詢SQLServer遞迴
- 記錄一次遞迴查詢的運用遞迴
- sql無限遞迴查詢SQL遞迴
- grep遞迴查詢子目錄遞迴
- sql遞迴查詢子級SQL遞迴
- oracle中connect by prior實現遞迴查詢Oracle遞迴
- 關於樹型結構資料遞迴查詢,轉非遞迴查詢的實現遞迴
- kingbase SQL最佳化案例 ( union遞迴 改 cte遞迴 )SQL遞迴
- 我對Postgresql遞迴查詢的理解SQL遞迴
- MS SQL Server的遞迴查詢(2)SQLServer遞迴
- java-遞迴(檔案查詢)Java遞迴
- SQL with as 的用法 以及遞迴函式的寫法 遞迴層次查詢SQL遞迴函式
- 遞迴的簡單應用遞迴
- 用VisualC++6.0遞迴查詢目錄樹 (轉)C++遞迴
- SQL2005查詢表中欄位的描述SQL
- 遞迴-M--二分查詢遞迴
- 遞迴樹形查詢所有分類遞迴
- 11GR2遞迴With子查詢遞迴
- Mysql 實現樹狀遞迴查詢MySql遞迴
- MYSQL: 表表示式(CTE)實現遞迴例項MySql遞迴
- 第二章 :查詢與排序-------二分查詢的遞迴解法排序遞迴
- iOS 演算法之排序、查詢、遞迴iOS演算法排序遞迴
- 資料庫遞迴查詢:MySQL VS Sequelize資料庫遞迴MySql