代碼
WITH paths(path, EmployeeID)
AS (
-- This section provides the value for the root of the hierarchy
SELECT hierarchyid::GetRoot() AS OrgNode, EmployeeID
FROM #Children AS C
WHERE ManagerID IS NULL
UNION ALL
-- This section provides values for all nodes except the root
SELECT
CAST(p.path.ToString() + CAST(C.Num AS varchar(30)) + '/' AS hierarchyid),
C.EmployeeID
FROM #Children AS C
JOIN paths AS p
ON C.ManagerID = P.EmployeeID
)
select * from Paths
1. 案例起因
公司門店應用程式每天都要出一份報表,用來統計任何商品當天的期初庫存數量、入庫數量、出庫數量
及當天的期末庫存數量。運行半年以後,這份報表運行越來越慢,到現在,每次運行該報表顯示當天資料時需要近20秒的時間。於是研發人員找到我,希望我看看,是不是能夠使該報表啟動並執行時間更短。
該報表就是一段SQL語句,主要由三部分組成,第一部分是計算每個商品的期初數量,第二部分是計算每個商品的當天發生(包括入庫和出庫的)數量,第三部分是計算每個商品的期末數量,也就是當天的餘額。每個部分使用UNION ALL串連起來。
我看到該報表,第一個感覺就是這段SQL裡的每個部分都要對錶進行掃描,明顯成本過高。應該能夠使用WITH AS進行改寫。
2. 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短語裡的資料放入一個全域暫存資料表裡。很多查詢通過這種方法都能夠提高速度。
3. 案例說明
首先介紹該SQL所涉及到的主要的表的結構。該表表名為fin,用來存放每天每個商品的發生數連同該商
品的餘額數。其表結構為如下所示(這裡我只選取了和我們要討論的SQL相關的部分表欄位)。
SQL> desc fin
名稱 是否為空白? 類型
----------------------------------------- -------- ----------------------------
。。。。。。
DAY DATE
SKU VARCHAR2(8)
INQTY NUMBER(16,6)
OUTQTY NUMBER(16,6)
LASTQTY NUMBER(16,6)
。。。。。。。。
簡單解釋一下各個欄位的含義:
1) DAY:發生的日期。
2) SKU:發生交易的商品代碼。
3) INQTY:商品入庫數量。
4) OUTQTY:商品出庫數量。
5) LASTQTY:商品的餘額數量。
該表中含有的記錄數量為:
SQL> SELECT count(*) FROM fin;
COUNT(*)
----------
4729319