【发布时间】:2017-01-02 10:37:39
【问题描述】:
我已包含一个用于复制/粘贴的简化数据集。将有一个链接组件表,以特定顺序链接。在生产中,该表将包含数十万条链,每条链包含 5-10 个组件。
输入也将是链,我将在数据库中搜索数据库链具有与输入链相同顺序的相同组件的任何实例。例如,如果数据库有链A,B,C,D,E,F,G和I输入链B和A,B,C和D,F,Z;结果会告诉我: 输入链 B 匹配 输入链 A、B、C 匹配 输入链 D、F、Z 不匹配
理想情况下,我希望能够输入数万条链来搜索数据库中的数十万条链。
我在 VB.Net 中有一个解决方案,它从数据库中获取与每个输入链的第一个组件匹配的所有链。然后它递归地向下导航数据库链,直到它到达末尾,或者它发现数据库链不匹配。它既不优雅也不高效,因为它一次只能查看一个输入链。我可以很容易地在 SQL 中编写一些使用 Cursor 达到相同效果的东西,但同样效率不高。
我正在尝试找到一种递归 CTE,它允许我一次运行数千个输入的整个查询。
这是一个简化的数据集:
DECLARE @Chains TABLE (ID int, Component varchar(15), ParentID int, DisplayOrder int, ChainID int)
DECLARE @SearchChains TABLE (Component varchar(15), DisplayOrder int, GroupID int)
INSERT INTO @Chains (ID, Component, ParentID, DisplayOrder, ChainID) VALUES (1, 'Head bone', 0, 0, 1)
INSERT INTO @Chains (ID, Component, ParentID, DisplayOrder, ChainID) VALUES (2, 'Neck bone', 1, 1, 1)
INSERT INTO @Chains (ID, Component, ParentID, DisplayOrder, ChainID) VALUES (3, 'Shoulder bone', 2, 2, 1)
INSERT INTO @Chains (ID, Component, ParentID, DisplayOrder, ChainID) VALUES (4, 'Back bone', 3, 3, 1)
INSERT INTO @Chains (ID, Component, ParentID, DisplayOrder, ChainID) VALUES (5, 'Hip bone', 4, 4, 1)
INSERT INTO @Chains (ID, Component, ParentID, DisplayOrder, ChainID) VALUES (6, 'Head bone', 0, 0, 2)
INSERT INTO @Chains (ID, Component, ParentID, DisplayOrder, ChainID) VALUES (7, 'Shoulder bone', 6, 1, 2)
INSERT INTO @SearchChains(Component, DisplayOrder, GroupID) VALUES ('Back bone', 0, 1)
INSERT INTO @SearchChains(Component, DisplayOrder, GroupID) VALUES ('Hip bone', 1, 1)
INSERT INTO @SearchChains(Component, DisplayOrder, GroupID) VALUES ('Leg bone', 2, 1)
INSERT INTO @SearchChains(Component, DisplayOrder, GroupID) VALUES ('Head bone', 0, 2)
INSERT INTO @SearchChains(Component, DisplayOrder, GroupID) VALUES ('Neck bone', 1, 2)
;
WITH cteMatching (ID, Component, ParentID, DisplayOrder, ChainID, RecLevel)
AS
(
SELECT C.ID, C.Component, C.ParentID, C.DisplayOrder, C.ChainID, 1 as RecLevel
FROM @Chains C
WHERE DisplayOrder = 0
UNION ALL
SELECT C.ID, C.Component, C.ParentID, C.DisplayOrder, C.ChainID, cte.RecLevel + 1
FROM @Chains C
INNER JOIN cteMatching cte ON C.ParentID = cte.ID
)
SELECT SC.Component, SC.DisplayOrder, SC.GroupID, cte.ID, cte.Component, cte.ParentID,
ISNULL(cte.DisplayOrder, 2147483647) as cteDisplayOrder, cte.ChainID, cte.RecLevel,
ISNULL((SELECT 1 WHERE SC.Component = cte.Component), 0) as IsMatch
FROM @SearchChains SC
LEFT OUTER JOIN cteMatching cte ON SC.Component = cte.Component
ORDER BY SC.GroupID ASC, cte.ChainID ASC, SC.DisplayOrder ASC, cteDisplayOrder ASC
结果是:
Component DisplayOrder GroupID ID Component ParentID cteDisplayOrder ChainID RecLevel IsMatch
--------------- ------------ ----------- ----------- --------------- ----------- --------------- ----------- ----------- -----------
Leg bone 2 1 NULL NULL NULL 2147483647 NULL NULL 0
Back bone 0 1 4 Back bone 3 3 1 4 1
Hip bone 1 1 5 Hip bone 4 4 1 5 1
Head bone 0 2 1 Head bone 0 0 1 1 1
Neck bone 1 2 2 Neck bone 1 1 1 2 1
Head bone 0 2 6 Head bone 0 0 2 1 1
这里的问题是 SearchChain GroupID 2 (Neck bone-> Head bone) 应该显示与数据库 ChainID 1 的匹配(它确实如此)并且它还应该显示 GroupID 2 和 ChainID 2、Neck 之间的 Head 组件匹配与 ChainID 2 中的 Shoulder 不匹配。但是当我注释掉 ChainID 1 和 SearchChain GroupID 1 时,ChainID 2 和 SearchChain GroupID 2 的结果是正确的。这让我很困惑。
目标是能够同时通过多个数据库链运行多个搜索链,目前,我的尝试失败了。有人有什么建议吗?
-E
【问题讨论】:
-
只是为了检查一下,如果我理解正确:两个表都导致独立的链。如果您在主链中的某处找到完整的链,则搜索表的链相当短并且它们是匹配的。正确的?您的链条是否有可能分裂、交叉或绕圈运行,或者它总是一条普通的链条?
-
是的,没错。输入链的长度通常要短得多,并且链始终是线性的,从头到尾没有循环或交叉。
-
我知道这不是你的真实数据,但结构有点奇怪。也就是说,您似乎有一个 Component 表、一个 Chain 表,然后您将搜索组件,而不管它们位于哪个链中。如果当前模式与您的问题空间匹配,那就足够了;我只是在观察。
-
嗨 Elroy,可能想listen into this :-)
-
Shnugo - 你找到了我的数据源 :)
标签: sql sql-server recursion common-table-expression