[英]SQL Server Rewrite Hierarchical CTE Function to a regular Select
我的任務是遷移遍歷層次結構並擴展它的腳本。 首先,腳本運行速度非常慢,其次我們正在進入一個更加受控制的服務器,因此我需要消除功能。 我想知道是否有人可以協助在第二個語句中集成函數正在執行的操作,並在第一個腳本的選擇語句中調用整個腳本。
我理解兩者之間的分離可能會更好地表現,但是這是唯一存在的功能和使用它的唯一選擇語句所以我更願意整合兩者而不是通過獲得批准的過程並補充說。 其次,如果有人能夠看到一種更優化的方式來實現這一目標,那將是很好的,我願意接受建議,記住這大約有11個級別。
腳本的第一部分是select語句,其中函數被調用並顯然返回到表:
DECLARE @RootNode INT = 1
DECLARE @Level1 INT = 2
DECLARE @Level2 INT = 3
DECLARE @Level3 INT = 4
DECLARE @Level4 INT = 5
TRUNCATE TABLE [...].[Hierarchy]
--
INSERT INTO [...].[Hierarchy]
SELECT Nodes.NodeId,
NodeTypeValues.Value AS HierarchyValue,
(select NodeTypeValue from [...].[Function_GetTheParentNodesForTheSelectedNodeType] (abc.NodeId, @RootNode)) AS RootLevel,
(select NodeTypeValue from [...].[Function_GetTheParentNodesForTheSelectedNodeType] (abc.NodeId, @Level1)) AS Level1,
(select NodeTypeValue from [...].[Function_GetTheParentNodesForTheSelectedNodeType] (abc.NodeId, @Level2)) AS Level2,
(select NodeTypeValue from [...].[Function_GetTheParentNodesForTheSelectedNodeType] (abc.NodeId, @Level3)) AS Level3,
(select NodeTypeValue from [...].[Function_GetTheParentNodesForTheSelectedNodeType] (abc.NodeId, @Level4)) AS Level4
--Level 5...
--Level 6...
--Level 7...
FROM [...].[Nodes] Nodes
INNER JOIN [...].NodeTypes NodeTypes ON NodeTypes.NodeTypeId = Nodes.NodeTypeId
INNER JOIN [...].NodeTypeValues NodeTypeValues ON NodeTypeValues.NodeTypeValueId = Nodes.NodeTypeValueId
WHERE NodeTypes.HierarchyTypeId = 1
第二部分是被調用的實際函數,該函數用於遍歷並將表結果返回給主查詢進行存儲:
FUNCTION [...].[Function_GetTheParentNodesForTheSelectedNodeType]
( @NodeId int,
@NodeTypeId int
)
RETURNS
@ReturnData TABLE
(
NodeTypeValue NVARCHAR(100),
NodeId INT
)
AS
BEGIN
WITH NodeSubTreesUpwards AS
(
SELECT SubRootNode.NodeId AS SubRootNodeId,
SubRootNode.*,
NULL AS ChildNodeId,
0 AS HierarchyLevel
FROM [...].[Nodes] AS SubRootNode
WHERE SubRootNode.NodeId = @NodeId
UNION ALL
SELECT NodeSubTreesUpwards.SubRootNodeId,
ParentNode.*,
Parent.ChildNodeId, (NodeSubTreesUpwards.HierarchyLevel) - 1 AS HierarchyLevel
FROM NodeSubTreesUpwards
INNER JOIN [...].[ParentChildNodes] AS Parent ON Parent.ChildNodeId = NodeSubTreesUpwards.NodeId
INNER JOIN [...].[Nodes] AS ParentNode ON ParentNode.NodeId = Parent.ParentNodeId
)
INSERT INTO @ReturnData
SELECT TOP 1 NodeTypeValues.Value, NodeSubTreesUpwards.NodeId
FROM NodeSubTreesUpwards NodeSubTreesUpwards
INNER JOIN [...].[NodeTypes] NodeType ON NodeType.NodeTypeId = n.NodeTypeId
INNER JOIN [...].[NodeTypeValues] NodeTypeValues ON NodeTypeValues.NodeTypeValueId = n.NodeTypeValueId
WHERE NodeType.NodeTypeId = @NodeTypeId
RETURN
我真的試圖把它分開,但一直在努力這樣做,我很可能錯過了一些愚蠢的東西,或者純粹只是不理解創建層次結構的過程,我現在已經坐了一兩天了。 我很樂意在不調用它的情況下使用相同的函數,而是在主select語句中代替被調用的函數,但不確定是否由於遞歸這將是一個問題?
嘗試使用內聯表值函數(ITVF),因為它們具有更好的執行計划。 MSDN上有一篇關於多語句表值函數的查詢性能問題的文章 :
- 通常,多語句TVF給出非常低的基數估計。
- 如果你使用多語句TVF,它就像另一個表一樣對待。 由於沒有可用的統計信息,SQL Server必須做出一些假設,並且通常會提供較低的估計值。 如果您的TVF只返回幾行,那就沒問題了。 但是如果你打算用數千行填充TVF,並且如果這個TVF與其他表連接,那么效率低的計划可能是由於低基數估計造成的。
因此,只需從多行語句函數Function_GetTheParentNodesForTheSelectedNodeType
創建兩個內聯表函數:
CREATE FUNCTION dbo.ufn_NodeSubTreesUpwards
( @NodeId int )
RETURNS table
AS
RETURN (
SELECT SubRootNode.NodeId AS SubRootNodeId,
SubRootNode.*,
NULL AS ChildNodeId,
0 AS HierarchyLevel
FROM [...].[Nodes] AS SubRootNode
WHERE SubRootNode.NodeId = @NodeId
UNION ALL
SELECT NodeSubTreesUpwards.SubRootNodeId,
ParentNode.*,
Parent.ChildNodeId, (NodeSubTreesUpwards.HierarchyLevel) - 1 AS HierarchyLevel
FROM NodeSubTreesUpwards
INNER JOIN [...].[ParentChildNodes] AS Parent
ON Parent.ChildNodeId = NodeSubTreesUpwards.NodeId
INNER JOIN [...].[Nodes] AS ParentNode ON ParentNode.NodeId = Parent.ParentNodeId
)
以及將在INSERT
查詢中使用的另一個函數:
CREATE FUNCTION dbo.ufn_GetTheParentNodesForTheSelectedNodeType
( @NodeId int,
@NodeTypeId int )
RETURNS table
AS
RETURN (
SELECT
TOP 1
NodeTypeValues.Value
, NodeSubTreesUpwards.NodeId
FROM ufn_NodeSubTreesUpwards(@NodeId) NodeSubTreesUpwards
INNER JOIN [...].[NodeTypes] NodeType ON NodeType.NodeTypeId = n.NodeTypeId
INNER JOIN [...].[NodeTypeValues] NodeTypeValues
ON NodeTypeValues.NodeTypeValueId = n.NodeTypeValueId
WHERE NodeType.NodeTypeId = @NodeTypeId
)
UPDATE - 在內聯表函數中使用遞歸cte的示例:
create function SequenceList ( @variable int )
returns table
as
return (
with cte as
(
select id = 1
union all
select id = cte.id+1
from cte
where id < @variable
)
select id from cte
--option ( maxrecursion 0 )
)
SELECT * FROM dbo.SequenceList(5)
事實上,整個劇本的表現非常糟糕。 每個函數調用都會從特定節點生成所有父關系,但只返回與節點類型過濾器對應的1行(它使用TOP 1
並且沒有ORDER BY
,因此他們假設變量過濾器生成所需行)。
執行插入的腳本只是“旋轉”節點的父級,這就是為什么有N個函數調用,每個調用更高級別。
我將第一個SELECT
(沒有INSERT
和變量)與函數的實現混合在一起,並在下面的SQL中用1表示所有相應的記錄。 每個CTE的簡要說明如下。
對於任何進一步的更正,我需要一個完全可復制的DML + DDL,我沒有正確的架構就做了我能做的事。
;WITH RecursionInputNodes AS
(
SELECT DISTINCT
Nodes.NodeId
FROM
[...].[Nodes] Nodes
INNER JOIN [...].NodeTypes NodeTypes ON NodeTypes.NodeTypeId = Nodes.NodeTypeId
INNER JOIN [...].NodeTypeValues NodeTypeValues ON NodeTypeValues.NodeTypeValueId = Nodes.NodeTypeValueId
WHERE
NodeTypes.HierarchyTypeId = 1
),
RecursiveCTE AS
(
-- CTE Anchor: Start with all input nodes at lvl 0
SELECT
SubRootNode.NodeId AS NodeId,
NULL AS ChildNodeId,
0 AS HierarchyLevel,
SubRootNode.NodeTypeId AS NodeTypeId,
NodeTypeValues.Value AS NodeTypeValue
FROM
RecursionInputNodes AS RI
INNER JOIN [...].[Nodes] AS SubRootNode ON RI.NodeID = RI.NodeId
INNER JOIN [...].[NodeTypes] NodeType ON NodeType.NodeTypeId = RI.NodeTypeId
INNER JOIN [...].[NodeTypeValues] NodeTypeValues ON NodeTypeValues.NodeTypeValueId = RI.NodeTypeValueId
UNION ALL
-- CTE Recursion: Add each node's parent and decrease lvl by 1 each time
SELECT
R.NodeId,
Parent.ChildNodeId,
R.HierarchyLevel - 1 AS HierarchyLevel,
ParentNode.NodeTypeId AS NodeTypeId,
NodeTypeValues.Value AS NodeTypeValue
FROM
RecursiveCTE AS R
INNER JOIN [...].[ParentChildNodes] AS Parent ON Parent.ChildNodeId = R.NodeId
INNER JOIN [...].[Nodes] AS ParentNode ON ParentNode.NodeId = Parent.ParentNodeId
INNER JOIN [...].[NodeTypes] NodeType ON NodeType.NodeTypeId = ParentNode.NodeTypeId
INNER JOIN [...].[NodeTypeValues] NodeTypeValues ON NodeTypeValues.NodeTypeValueId = ParentNode.NodeTypeValueId
),
Just1RowByNodeTypeByNode AS
(
SELECT
R.NodeId,
R.NodeTypeId,
NodeTypeValue = MAX(R.NodeTypeValue) -- I'm "imitating" the TOP 1 from the function here
FROM
RecursiveCTE AS R
GROUP BY
R.NodeId,
R.NodeTypeId
)
SELECT
Nodes.NodeId,
NodeTypeValues.Value AS HierarchyValue,
L1.NodeTypeValue AS RootLevel,
L2.NodeTypeValue AS Level1, -- Note that the alias Level 1 here actually corresponds to the value 2 for NodeTypeId
L3.NodeTypeValue AS Level2,
L4.NodeTypeValue AS Level3,
L5.NodeTypeValue AS Level4
--Level 5...
--Level 6...
--Level 7...
FROM
RecursionInputNodes Nodes
INNER JOIN [...].NodeTypes NodeTypes ON NodeTypes.NodeTypeId = Nodes.NodeTypeId
INNER JOIN [...].NodeTypeValues NodeTypeValues ON NodeTypeValues.NodeTypeValueId = Nodes.NodeTypeValueId
LEFT JOIN Just1RowByNodeTypeByNode AS L1 ON Nodes.NodeId = L1.NodeId AND L1.NodeTypeId = 1
LEFT JOIN Just1RowByNodeTypeByNode AS L2 ON Nodes.NodeId = L2.NodeId AND L2.NodeTypeId = 2
LEFT JOIN Just1RowByNodeTypeByNode AS L3 ON Nodes.NodeId = L3.NodeId AND L3.NodeTypeId = 3
LEFT JOIN Just1RowByNodeTypeByNode AS L4 ON Nodes.NodeId = L4.NodeId AND L4.NodeTypeId = 4
LEFT JOIN Just1RowByNodeTypeByNode AS L5 ON Nodes.NodeId = L5.NodeId AND L5.NodeTypeId = 5
RecursionInputNodes
保存RecursionInputNodes
的輸入節點列表。 RecursiveCTE
是具有父關系的所有輸入節點的集合,直到不再存在。 父關系通過Parent.ChildNodeId = R.NodeId
。 我還添加了NodeTypeId
和NodeTypeValue
因為我們需要在下一個CTE上過濾它們。 Just1RowByNodeTypeByNode
來確定,每個NodeId
和NodeTypeId
的通緝值NodeTypeValue
,這是主叫方從功能所需要的。 NodeTypeId
將被過濾(它是原始函數的參數)。 此步驟“模仿”原始功能的TOP 1
。 我建議按順序逐個執行每個CTE(每個CTE都有前一個,因為它們被引用)以了解最后一個SELECT
如何一起使用。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.