T-SQL with as 的用法(轉) SQL 下的遞歸查詢 SQL2005(CTE) ,SQL2000(Function 遞歸)


 

摘自: http://blog.csdn.net/bluefoxev/article/details/6779794

 

------- SQL2005 方法

一.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語句外,還可以進行遞歸調用,關於這一部分的內容將在下一篇文章中介紹。

 

------- SQL2000 方法

摘自: http://bbs.csdn.net/topics/360215000

 

if object_id('[tb]') is not null drop table [tb]
go
create table [tb]([modeid] int,modename varchar(20),parentid int)
insert [tb]
select 100 ,'商品管理', 0 union all
select 101 ,'定單管理', 0 union all
select 102 ,'用戶管理', 0 union all
select 104 ,'學院廣告', 0 union all
select 105 ,'系統設置', 0 union all
select 106 ,'附件管理', 0 union all
select 107 ,'商品管理', 100 union all
select 108 ,'明細管理', 100 union all
select 109 ,'物流管理', 100 union all
select 110 ,'商品信息管理', 107 union all
select 111 ,'商品分類管理', 107 union all
select 112 ,'回收站管理', 107 union all
select 114 ,'團購管理', 108 union all
select 115 ,'拍賣管理', 108 union all
select 116 ,'優惠管理', 108 union all
select 117 ,'會員管理', 102 union all
select 118 ,'會員卡管理', 102 union all
select 119 ,'資金管理', 102 union all
select 120 ,'管理員管理', 102 union all
select 121 ,'添加管理員', 120 union all
select 122 ,'修改管理員', 120
go
 
 
--查所有子結點
if object_id('f_getC') is not null drop function f_getC
go
create function f_getC(@id int) 
returns @re table(id int,level int,sort varchar(10)) 
as 
begin
    declare @l int 
    set @l=0 
    insert @re select @id,@l,null
    while @@rowcount>0
    begin 
        set @l=@l+1
        insert @re select a.modeid,@l,ltrim(isnull(b.sort,a.modeid)) from tb as a,@re as b 
 where b.id=a.parentid and b.level=@l-1
    end
    update @re set level = level -1
    return 
end 
go 
 
 
select a.modeid,a.parentid,REPLICATE('  ',b.level) +''+a.modename,b.level,b.sort from tb  a,f_getC(0) b 
where a.modeid=b.id 
order by case when b.level<2 then 0 else 1 end,b.sort,b.level
 
/*
modeid      parentid                                                       sort       level      
----------- ----------- -------------------------------------------------- ---------- -----------
100         0           ┝商品管理                                              100        0
107         100           ┝商品管理                                            100        1
108         100           ┝明細管理                                            100        1
109         100           ┝物流管理                                            100        1
101         0           ┝定單管理                                              101        0
102         0           ┝用戶管理                                              102        0
117         102           ┝會員管理                                            102        1
118         102           ┝會員卡管理                                           102        1
119         102           ┝資金管理                                            102        1
120         102           ┝管理員管理                                           102        1
104         0           ┝學院廣告                                              104        0
105         0           ┝系統設置                                              105        0
106         0           ┝附件管理                                              106        0
110         107             ┝商品信息管理                                        100        2
111         107             ┝商品分類管理                                        100        2
112         107             ┝回收站管理                                         100        2
114         108             ┝團購管理                                          100        2
115         108             ┝拍賣管理                                          100        2
116         108             ┝優惠管理                                          100        2
121         120             ┝添加管理員                                         102        2
122         120             ┝修改管理員                                         102        2
 
(所影響的行數為 21 行)
 
*/
 
 
--查所有子結點,帶路徑與排序
if object_id('f_getC') is not null drop function f_getC
go
create function f_getC(@id int) 
returns @re table(id int,level int,sort varchar(100),path varchar(500)) 
as 
begin
    declare @l int 
    set @l=0 
    insert @re
 select [modeid],@l,right('00000'+ltrim(modeid),5),modename
 from tb where parentid=@id
    while @@rowcount>0
    begin 
        set @l=@l+1
        insert @re
  select a.modeid,@l,b.sort+right('00000'+ltrim(a.modeid),5),
      b.path+' - '+a.modename
  from tb as a,@re as b 
  where b.id=a.parentid and b.level=@l-1
    end
    update @re set level = level
    return 
end 
go 
 
select a.modeid,a.parentid,REPLICATE('  ',b.level) +''+a.modename,b.level,b.sort ,b.path from tb  a,f_getC(0) b 
where a.modeid=b.id 
order by sort
 
/*
modeid      parentid                         level                           
----------- ----------- -------------------- ----------- -------------------- ----------------------------------------
100         0           ┝商品管理                0           00100                商品管理
107         100           ┝商品管理              1           0010000107           商品管理 - 商品管理
110         107             ┝商品信息管理          2           001000010700110      商品管理 - 商品管理 - 商品信息管理
111         107             ┝商品分類管理          2           001000010700111      商品管理 - 商品管理 - 商品分類管理
112         107             ┝回收站管理           2           001000010700112      商品管理 - 商品管理 - 回收站管理
108         100           ┝明細管理              1           0010000108           商品管理 - 明細管理
114         108             ┝團購管理            2           001000010800114      商品管理 - 明細管理 - 團購管理
115         108             ┝拍賣管理            2           001000010800115      商品管理 - 明細管理 - 拍賣管理
116         108             ┝優惠管理            2           001000010800116      商品管理 - 明細管理 - 優惠管理
109         100           ┝物流管理              1           0010000109           商品管理 - 物流管理
101         0           ┝定單管理                0           00101                定單管理
102         0           ┝用戶管理                0           00102                用戶管理
117         102           ┝會員管理              1           0010200117           用戶管理 - 會員管理
118         102           ┝會員卡管理             1           0010200118           用戶管理 - 會員卡管理
119         102           ┝資金管理              1           0010200119           用戶管理 - 資金管理
120         102           ┝管理員管理             1           0010200120           用戶管理 - 管理員管理
121         120             ┝添加管理員           2           001020012000121      用戶管理 - 管理員管理 - 添加管理員
122         120             ┝修改管理員           2           001020012000122      用戶管理 - 管理員管理 - 修改管理員
104         0           ┝學院廣告                0           00104                學院廣告
105         0           ┝系統設置                0           00105                系統設置
106         0           ┝附件管理                0           00106                附件管理
 
(21 行受影響)
 
 
*/
 
----------

 

還是這個腳本, SQL 2005 中的用法.

 
         

if object_id('[tb]') is not null drop table [tb]
go
create table [tb]([modeid] int,modename varchar(20),parentid int)
insert [tb]
select 100 ,'商品管理', 0 union all
select 101 ,'定單管理', 0 union all
select 102 ,'用戶管理', 0 union all
select 104 ,'學院廣告', 0 union all
select 105 ,'系統設置', 0 union all
select 106 ,'附件管理', 0 union all
select 107 ,'商品管理', 100 union all
select 108 ,'明細管理', 100 union all
select 109 ,'物流管理', 100 union all
select 110 ,'商品信息管理', 107 union all
select 111 ,'商品分類管理', 107 union all
select 112 ,'回收站管理', 107 union all
select 114 ,'團購管理', 108 union all
select 115 ,'拍賣管理', 108 union all
select 116 ,'優惠管理', 108 union all
select 117 ,'會員管理', 102 union all
select 118 ,'會員卡管理', 102 union all
select 119 ,'資金管理', 102 union all
select 120 ,'管理員管理', 102 union all
select 121 ,'添加管理員', 120 union all
select 122 ,'修改管理員', 120
go





with
cr(modeid,modename,parentid,layer) as ( select modeid,modename, parentid, cast( modeid as varchar) as layer from [tb] where parentid = 0 union all select tb.modeid,tb.modename,tb.parentid,cast(CAST( cr.layer as varchar) + cast(tb.modeid as varchar) as varchar) as layer from tb inner join cr on tb.parentid = cr.modeid ) select * from cr order by layer

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM