FastReport教程:MS SQL中的遞歸

下載FastReport.Net最新版本

有時,需要存儲過程或函數才能多次使用樣本的結果。在這種情況下,我們經常使用臨時表。但是,值得考慮臨時表的一些優點和缺點。

好處:

  • 臨時表是完整的表。因此,您可以爲它們創建索引和統計信息。這可以顯着加快他們的工作。

缺點:

  • 填寫與數據移動相關的臨時表。雖然這是一個簡單的插入操作,但磁盤上仍然存在大量數據的負載;

  • 存在查詢執行時間增加的風險。臨時表在tempdb數據庫中創建。而且這個基地的負荷很大。

考慮到使用臨時表的風險,使用通用表表達式看起來更具吸引力。

通用表表達式

公用表表達式(CTE)是一個帶有公用表的表達式,可以在查詢中多次使用。CTE不會保存數據,但會創建類似臨時視圖的內容。有人可能會說CTE是主查詢之前的子查詢。但這並不完全正確,因爲子查詢不能多次使用,但是,CTE可以。

在哪些情況下使用通用表表達式更好?

  1. 創建遞歸查詢,使用它可以以分層形式獲取數據;

  2. 在同一查詢中多次引用數據集;

  3. 爲了替換視圖,臨時表,表變量。

CTE的優點包括:遞歸,高速查詢,簡潔查詢。

缺點只能在有限的使用中。CTE只能用於它所屬的查詢。您不能在其他查詢中使用它。在這種情況下,您將不得不使用臨時表或表變量。 通用表表達式簡單且遞歸。 簡單的不包括對自己的引用,並且遞歸分別包括。 遞歸CTE用於返回分層數據。 考慮一個簡單CTE語句的示例:

 WITH CTEQuery (Field1, Field2)
 AS
 (
 SELECT (Field1, Field2) FROM TABLE
 )
 SELECT * FROM CTEQuery

這裏CTEQuery是CTE的名稱;

  • Field1,Field2 - 請求的字段名稱;

  • Table - 從中選擇數據以在主查詢中使用的一些表。

在此示例中,可以而不是顯式指定選擇字段,因爲我們從TestTable表中選擇所有字段:

WITH CTEQuery
 AS
 (
 SELECT * FROM Table
 )
SELECT * FROM CTEQuery

在CTE的幫助下,如果取出CTE中的部分邏輯,則可以優化主查詢。事實是,CTE允許您一次創建多個表達式(查詢)。因此,您可以使用CTE將複雜查詢拆分爲幾個初步“View”,然後將它們鏈接到一個公共查詢中:

WITH CTEQuery1 (Field1, Field2) AS
(
 SELECT Field1 AS ID, Field2 FROM Table1
 WHERE Field2 >= 1000
),
CTEQuery2 (Field3, Field4) AS
(
 SELECT Field3 AS ID, Field4 FROM Table2
 WHERE Field4 = 'Москва'
)
 
SELECT * FROM CTEQuery1 INNER JOIN CTEQuery2 ON CTEQuery2.ID = CTEQuery1.ID

如上所述,CTE的主要目的是遞歸。遞歸的典型任務是樹遍歷。所以我們可以在“with”的幫助下構建一棵樹。遞歸查詢結構首先出現在SQL Server 2005中。 看一下WITH語句:

WITH RecursiveQuery AS
(
 {Anchor}
 UNION ALL
 {Joined TO RecursiveQuery}
)
SELECT * FROM RecursiveQuery

{Anchor} - anchor,一個定義樹的初始元素的查詢(分層列表)。通常在錨中有一個WHERE子句,用於定義表的特定行。 在UNION ALL之後,目標表從JOIN跟隨到CTE表達式。 {加入RecursiveQuery} - 從目標表中選擇。這通常與錨點中使用的表相同。但是在這個查詢中,它連接到CTE表達式,形成遞歸。此連接的條件決定了父子關係。這取決於你是去樹的上層還是下層。 讓我們看一個返回組織單元列表的遞歸查詢。準備此請求的數據:

CREATE TABLE Department
(
ID INT,
ParentID INT,
Name VARCHAR(50)
)
 
INSERT INTO Department ( ID, ParentID, Name ) 
VALUES (1, 0, 'Finance Director')
INSERT INTO Department ( ID, ParentID, Name ) 
VALUES (2, 1, 'Deputy Finance Director')
INSERT INTO Department ( ID, ParentID, Name ) 
VALUES (3, 1, 'Assistance Finance Director')
INSERT INTO Department ( ID, ParentID, Name ) 
VALUES (4, 3, 'Executive Bodget Office')
INSERT INTO Department ( ID, ParentID, Name ) 
VALUES (5, 3, 'Comptroller')
INSERT INTO Department ( ID, ParentID, Name ) 
VALUES (6, 3, 'Purchasing')
INSERT INTO Department ( ID, ParentID, Name ) 
VALUES (7, 3, 'Debt Management')
INSERT INTO Department ( ID, ParentID, Name ) 
VALUES (8, 3, 'Risk Management')
INSERT INTO Department ( ID, ParentID, Name ) 
VALUES (9, 2, 'Public Relations')
INSERT INTO Department ( ID, ParentID, Name ) 
VALUES (10, 2, 'Finance Personnel')
INSERT INTO Department ( ID, ParentID, Name ) 
VALUES (11, 2, 'Finance Accounting')
INSERT INTO Department ( ID, ParentID, Name ) 
VALUES (12, 2, 'Liasion to Boards and Commissions')

已經清楚的是,組織中的分支結構是分層的。我們的任務是獲得一份隸屬於財務總監助理的部門清單。如果我們在分層樹的上下文中進行討論,那麼我們必須找到一個分支及其葉子。 但首先,讓我們看看整個分部列表:

ID

ParentID

Name

1

0

Finance Director

2

1

Deputy Finance Director

3

1

Assistance Finance Director

4

3

Executive Bodget Office

5

3

Comptroller

6

3

Purchasing

7

3

Debt Management

8

3

Risk Management

9

2

Public Relations

10

2

Finance Personnel

11

2

Finance Accounting

12

2

Liasion to Boards and Commissions

頭部有財務總監,副手和助理報表給他。他們每個人在其管轄範圍內都有一組單位。ParentID字段指示“主機”標識符。因此,我們有一個現成的主從連接。 讓我們用WITH編寫一個遞歸查詢。

WITH RecursiveQuery (ID, ParentID, Name)
AS
(
 SELECT ID, ParentID, Name
 FROM Department dep
 WHERE dep.ID = 3
 UNION ALL
 SELECT dep.ID, dep.ParentID, dep.Name
 FROM Department dep
 JOIN RecursiveQuery rec ON dep.ParentID = rec.ID
)
SELECT ID, ParentID, Name
FROM RecursiveQuery

在此示例中,清楚地指示了要在CTE中選擇的字段的名稱。但是,內部查詢具有相同的字段。因此,您只需刪除此列表以及括號即可。 在CTE內部,我們有兩個類似的查詢。第一個選擇我們正在構建的樹的根元素。第二個是所有後續的從屬元素,因爲它與CTE本身有關。SQL中的“遞歸”實際上不是遞歸,而是迭代。您需要以JOIN作爲循環提交查詢,然後一切都將立即清除。在每次迭代中,我們都知道前一個樣本的值並獲得從屬元素。在下一步中,我們獲得前一個樣本的從屬元素。也就是說,每次迭代都是向下或向上轉換,具體取決於通信條件。 上述查詢的結果是:

ID

ParentID

Name

3

1

Assistance Finance Director

4

3

Executive Bodget Office

5

3

Comptroller

6

3

Purchasing

7

3

Debt Management

8

3

Risk Management

但是如果不使用CTE,這個查詢會是什麼樣子:

DECLARE @Department TABLE (ID INT, ParentID INT, Name VARCHAR(50), Status INT DEFAULT 0)
-- First, we select the anchor in the table variable - the initial element from which we build the tree.
INSERT @Department
SELECT ID, ParentID, Name, 0
 FROM Department dep
 WHERE dep.ID = 3
 
DECLARE @rowsAdded INT = @@ROWCOUNT
-- We are going through a cycle until new departments are added in the previous step.
WHILE @rowsAdded > 0 
BEGIN
-- Mark entries in a table variable as ready for processing
UPDATE @Department SET Status = 1 WHERE Status = 0
-- Select child records for the previous record
INSERT @Department 
SELECT dep.ID, dep.ParentID, dep.Name, 0
 FROM Department dep
 JOIN @Department rec ON dep.ParentID = rec.ID
AND rec.Status = 1
SET @rowsAdded = @@ROWCOUNT 
 -- Mark entries found in the current step as processed 
 UPDATE @Department SET Status = 2 WHERE Status = 1 
END
SELECT * FROM @Department

這樣的循環比CTE表達慢得多。此外,它需要創建一個表變量。並且代碼量增加了一倍。因此,CTE表達式是MS SQL中遞歸樹遍歷的最佳解決方案。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章