• <ruby id="5koa6"></ruby>
    <ruby id="5koa6"><option id="5koa6"><thead id="5koa6"></thead></option></ruby>

    <progress id="5koa6"></progress>

  • <strong id="5koa6"></strong>
    • 軟件測試技術
    • 軟件測試博客
    • 軟件測試視頻
    • 開源軟件測試技術
    • 軟件測試論壇
    • 軟件測試沙龍
    • 軟件測試資料下載
    • 軟件測試雜志
    • 軟件測試人才招聘
      暫時沒有公告

    字號: | 推薦給好友 上一篇 | 下一篇

    軟件測試中SQL2005/2008中的CTE應用--遞歸查詢

    發布: 2010-6-29 08:33 | 作者: 網絡轉載 | 來源: 領測軟件測試網采編 | 查看: 35次 | 進入軟件測試論壇討論

    領測軟件測試網

    軟件測試中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,直到生成最終結果集?梢栽谟脩舳x的例程(如函數、存儲過程、觸發器或視圖)中定義 CTE。

    2、公用表表達式 (CTE) 具有一個重要的優點,那就是能夠引用其自身,從而創建遞歸 CTE 。遞歸 CTE 是一個重復執行初始 CTE 以返回數據子集直到獲取完整結果集的公用表表達式。當某個查詢引用遞歸 CTE 時,它即被稱為遞歸查詢 。遞歸查詢通常用于返回分層數據,例如:顯示某個組織圖中的雇員或物料清單方案(其中父級產品有一個或多個組件,而那些組件可能還有子組件,或者是其他父級產品的組件)中的數據。

    遞歸 CTE 可以極大地簡化在 SELECT、INSERT、UPDATE、DELETE 或 CREATE VIEW 語句中運行遞歸查詢所需的代碼。在 SQL Server 的早期版本中,遞歸查詢通常需要使用臨時表、游標和邏輯來控制遞歸步驟流。有關公用表表達式的詳細信息,請參閱使用公用表表達式 。

    這里舉例說明如下:

    為了描述方便,邀月特地列舉了一個常見的自關聯Table

    表結構如下:
    view plaincopy to clipboardprint?
    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
    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

    再插入一些測試數據

    + expand sourceview plaincopy to clipboardprint?
    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,'邀月備注'
    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的分類包含所有子分類。也許很多情況下,我們不得不用臨時表或表變量等,F在我們有了CTE,就簡單多了

    view plaincopy to clipboardprint?
    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
    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語句:

    view plaincopy to clipboardprint?
    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
    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的分類包含所有子分類:

    view plaincopy to clipboardprint?
    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/%'
    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

    view plaincopy to clipboardprint?
    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
    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

    查詢結果同上,略去。這里我們看出,有時候,好的表結構設計相當重要。

    有人很關心性能問題。目前沒有測試過。稍后會附上百萬級測試報告。不過,有兩點理解邀月忘了補充:

    一、CTE其實是面向對象的,運行的基礎是CLR。一個很好的說明是With查詢語句中是區分字段的大小寫的 。即"C_Code"和"c_Code"是不一樣的,后者會報錯。這與普通的SQL語句不同 。

    二、 這個應用示例重在簡化業務邏輯 ,即便是性能不佳,但對臨時表\表變量\游標等傳統處理方式是一種業務層次上的簡化或者說是優化。

    延伸閱讀

    文章來源于領測軟件測試網 http://www.kjueaiud.com/

    TAG: 查詢 軟件測試 應用 CTE 遞歸


    關于領測軟件測試網 | 領測軟件測試網合作伙伴 | 廣告服務 | 投稿指南 | 聯系我們 | 網站地圖 | 友情鏈接
    版權所有(C) 2003-2010 TestAge(領測軟件測試網)|領測國際科技(北京)有限公司|軟件測試工程師培訓網 All Rights Reserved
    北京市海淀區中關村南大街9號北京理工科技大廈1402室 京ICP備10010545號-5
    技術支持和業務聯系:info@testage.com.cn 電話:010-51297073

    軟件測試 | 領測國際ISTQBISTQB官網TMMiTMMi認證國際軟件測試工程師認證領測軟件測試網

    老湿亚洲永久精品ww47香蕉图片_日韩欧美中文字幕北美法律_国产AV永久无码天堂影院_久久婷婷综合色丁香五月

  • <ruby id="5koa6"></ruby>
    <ruby id="5koa6"><option id="5koa6"><thead id="5koa6"></thead></option></ruby>

    <progress id="5koa6"></progress>

  • <strong id="5koa6"></strong>