關(guān)于使用CTE(公用表表達(dá)式)的遞歸查詢----SQL Server 2005及以上版本
公用表表達(dá)式 (CTE) 具有一個重要的優(yōu)點(diǎn),那就是能夠引用其自身,從而創(chuàng)建遞歸 CTE。遞歸 CTE 是一個重復(fù)執(zhí)行初始 CTE 以返回?cái)?shù)據(jù)子集直到獲取完整結(jié)果集的公用表表達(dá)式。
當(dāng)某個查詢引用遞歸 CTE 時,它即被稱為遞歸查詢。遞歸查詢通常用于返回分層數(shù)據(jù),例如:顯示某個組織圖中的雇員或物料清單方案(其中父級產(chǎn)品有一個或多個組件,而那些組件可能還有子組件,或者是其他父級產(chǎn)品的組件)中的數(shù)據(jù)。
遞歸 CTE 可以極大地簡化在 SELECT、INSERT、UPDATE、DELETE 或 CREATE VIEW 語句中運(yùn)行遞歸查詢所需的代碼。在 SQL Server 的早期版本中,遞歸查詢通常需要使用臨時表、游標(biāo)和邏輯來控制遞歸步驟流?!?/P>
CTE 的基本語法結(jié)構(gòu)如下:
復(fù)制代碼 代碼如下:
WITH expression_name [ ( column_name [,...n] ) ]
AS
( CTE_query_definition )
--只有在查詢定義中為所有結(jié)果列都提供了不同的名稱時,列名稱列表才是可選的。
--運(yùn)行 CTE 的語句為:
SELECT column_list> FROM expression_name;
在使用CTE時應(yīng)注意如下幾點(diǎn):
CTE后面必須直接跟使用CTE的SQL語句(如select、insert、update等),否則,CTE將失效。如下面的SQL語句將無法正常使用CTE:
復(fù)制代碼 代碼如下:
with
cr as
?。?
select * from 表名 where 條件
)
--select * from person.CountryRegion --如果加上這句話后面用到cr將報(bào)錯
select * from cr
2. CTE后面也可以跟其他的CTE,但只能使用一個with,多個CTE中間用逗號(,)分隔,如下面的SQL語句所示:
復(fù)制代碼 代碼如下:
with
cte1 as
(
select * from table1 where name like '測試%'
),
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á)式名稱與某個數(shù)據(jù)表或視圖重名,則緊跟在該CTE后面的SQL語句使用的仍然是CTE,當(dāng)然,后面的SQL語句使用的就是數(shù)據(jù)表或視圖。
4. CTE 可以引用自身,也可以引用在同一 WITH 子句中預(yù)先定義的 CTE。
5. 不能在 CTE_query_definition 中使用以下子句:
復(fù)制代碼 代碼如下:
COMPUTE 或 COMPUTE BY
ORDER BY(除非指定了 TOP 子句)
INTO
帶有查詢提示的 OPTION 子句
FOR XML
FOR BROWSE
6. 如果將 CTE 用在屬于批處理的一部分的語句中,那么在它之前的語句必須以分號結(jié)尾,如下面的SQL所示:
復(fù)制代碼 代碼如下:
declare @s nvarchar(3)
set @s = '測試%'; -- 必須加分號
with
t_tree as
(
select * from 表 where 字段 like @s
)
select * from t_tree
------------------------------------操作------------------------------------
上面可能對with as說的有點(diǎn)兒啰嗦了,下面進(jìn)入正題:
老規(guī)矩先建表(Co_ItemNameSet):
復(fù)制代碼 代碼如下:
CREATE TABLE [dbo].[Co_ItemNameSet](
[ItemId] [int] NULL,
[ParentItemId] [int] NULL,
[ItemName] [nchar](10) COLLATE Chinese_PRC_CI_AS NULL
) ON [PRIMARY]
插入數(shù)據(jù):
復(fù)制代碼 代碼如下:
--給表插入數(shù)據(jù)
insert into dbo.Co_ItemNameSet values(2,0,'管理費(fèi)用')
insert into dbo.Co_ItemNameSet values(3,0,'銷售費(fèi)用')
insert into dbo.Co_ItemNameSet values(4,0,'財(cái)務(wù)費(fèi)用')
insert into dbo.Co_ItemNameSet values(5,0,'生產(chǎn)成本')
insert into dbo.Co_ItemNameSet values(35,5,'材料')
insert into dbo.Co_ItemNameSet values(36,5,'人工')
insert into dbo.Co_ItemNameSet values(37,5,'制造費(fèi)用')
insert into dbo.Co_ItemNameSet values(38,35,'原材料')
insert into dbo.Co_ItemNameSet values(39,35,'主要材料')
insert into dbo.Co_ItemNameSet values(40,35,'間輔材料')
insert into dbo.Co_ItemNameSet values(41,36,'工資')
insert into dbo.Co_ItemNameSet values(42,36,'福利')
insert into dbo.Co_ItemNameSet values(43,2,'管理費(fèi)用子項(xiàng)')
insert into dbo.Co_ItemNameSet values(113,43,'管理費(fèi)用子項(xiàng)的子項(xiàng)')
查詢插入的數(shù)據(jù):
復(fù)制代碼 代碼如下:
--查詢數(shù)據(jù)
select * from Co_ItemNameSet
結(jié)果圖:
題目需求是:查詢ItemId=2及子節(jié)點(diǎn),也就是管理費(fèi)用和其下屬所有節(jié)點(diǎn)的信息
操作1:先看看不用CTE遞歸操作的sql語句如下(需要真是的建兩個表進(jìn)行數(shù)據(jù)的存放和判斷,非常麻煩):
復(fù)制代碼 代碼如下:
declare @i int
select @i=2;
create table #tem(
[ItemId] [INT] NOT NULL,
[level] INT
);
create table #list(
[ItemId] [INT] NOT NULL,
[ParentItemId] [INT] NOT NULL default ((0)),
[ItemName] [nvarchar](100) NOT NULL default (''),
[level] int
);
insert INTO #tem([ItemId],[level])
select ItemId,1
from Co_ItemNameSet
where itemid=@i
insert into #list([ItemId],[ParentItemId],[ItemName],[level])
select ItemId,ParentItemId,ItemName,1
from Co_ItemNameSet
where itemid=@i
declare @level int
select @level=1
declare @current INT
select @current=0
while(@level>0)
begin
select @current=ItemId
from #tem
where [level]=@level
if @@ROWCOUNT>0
begin
delete from #tem
where [level]=@level and ItemId=@current
insert into #tem([ItemId],[level])
select [ItemId],@level+1
from Co_ItemNameSet
where ParentItemId=@current
insert into #list([ItemId],[ParentItemId],[ItemName],[level])
select [ItemId],[ParentItemId],[ItemName],@level+1
from Co_ItemNameSet
where ParentItemId=@current
if @@rowcount>0
begin
select @level=@level+1
end
end
else
begin
select @level=@level-1
end
end
select * from #list
drop table #tem
drop table #list
結(jié)果圖:
操作2:用CTE遞歸操作的sql語句如下:
復(fù)制代碼 代碼如下:
DECLARE @i INT
SELECT @i=2;
WITH Co_ItemNameSet_CTE(ItemId,ParentItemId,ItemName,[Level])
AS
(
SELECT ItemId,ParentItemId,ItemName,1 AS [Level]
FROM Co_ItemNameSet
WHERE itemid=@i
UNION ALL
SELECT c.ItemId,c.ParentItemId,c.ItemName,[Level] + 1
FROM Co_ItemNameSet c INNER JOIN Co_ItemNameSet_CTE ct
ON c.ParentItemId=ct.ItemId
)
SELECT * FROM Co_ItemNameSet_CTE
結(jié)果圖:
-----------------------------分析(查看MSDN的分析)----------------------------
主要分析一下用CTE的遞歸操作:
遞歸 CTE 由下列三個元素組成:
例程的調(diào)用。
遞歸 CTE 的第一個調(diào)用包括一個或多個由 UNION ALL、UNION、EXCEPT 或 INTERSECT 運(yùn)算符聯(lián)接的 CTE_query_definitions。由于這些查詢定義形成了 CTE 結(jié)構(gòu)的基準(zhǔn)結(jié)果集,所以它們被稱為“定位點(diǎn)成員”。
CTE_query_definitions 被視為定位點(diǎn)成員,除非它們引用了 CTE 本身。所有定位點(diǎn)成員查詢定義必須放置在第一個遞歸成員定義之前,而且必須使用 UNION ALL 運(yùn)算符聯(lián)接最后一個定位點(diǎn)成員和第一個遞歸成員。
例程的遞歸調(diào)用。
遞歸調(diào)用包括一個或多個由引用 CTE 本身的 UNION ALL 運(yùn)算符聯(lián)接的 CTE_query_definitions(就是as里的語句塊)。這些查詢定義被稱為“遞歸成員”。
終止檢查。
終止檢查是隱式的;當(dāng)上一個調(diào)用中未返回行時,遞歸將停止。
遞歸 CTE 結(jié)構(gòu)必須至少包含一個定位點(diǎn)成員和一個遞歸成員。以下偽代碼顯示了包含一個定位點(diǎn)成員和一個遞歸成員的簡單遞歸 CTE 的組件。
復(fù)制代碼 代碼如下:
WITH cte_name ( column_name [,...n] )
AS
(
CTE_query_definition --定位點(diǎn)成員
UNION ALL
CTE_query_definition --遞歸成員.
)
現(xiàn)在讓我們看一下遞歸執(zhí)行過程:
將 CTE 表達(dá)式拆分為定位點(diǎn)成員和遞歸成員。
運(yùn)行定位點(diǎn)成員,創(chuàng)建第一個調(diào)用或基準(zhǔn)結(jié)果集 (T0)。
運(yùn)行遞歸成員,將 Ti 作為輸入,將 Ti+1 作為輸出。
重復(fù)步驟 3,直到返回空集。
返回結(jié)果集。這是對 T0 到 Tn 執(zhí)行 UNION ALL 的結(jié)果。
您可能感興趣的文章:- SQLserver2008使用表達(dá)式遞歸查詢
- sqlserver另類非遞歸的無限級分類(存儲過程版)
- SQLSERVER2005 中樹形數(shù)據(jù)的遞歸查詢
- 使用SQLSERVER 2005/2008 遞歸CTE查詢樹型結(jié)構(gòu)的方法
- sqlserver中存儲過程的遞歸調(diào)用示例
- 使用SqlServer CTE遞歸查詢處理樹、圖和層次結(jié)構(gòu)