我有以下两个表E
和 G
.
create table E(K1 int, K2 int primary key (K1, K2))
insert E
values (1, 11), (1, 20), (2, 10), (2, 30), (3, 10), (3, 30),
(4, 100), (5, 200), (6, 200),
(7, 300), (8, 300), (9, 310), (10, 310), (10, 320), (11, 320), (12, 330)
create table G(GroupID varchar(10), K1 int primary key)
insert G
values ('Group 1', 1), ('Group 1', 2), ('Group 2', 4), ('Group 2', 5),
('Group 3', 8), ('Group 3', 9), ('Group 3', 12)
我需要一个 View - 给出 K2
号,查找所有相关的K1
. “相关K1”定义:
所有
K1
我们有相同的K2
在表中E
.例如E
中的 2 和 3是相关的,因为两条记录都有K2
共 10 个 ((2, 10), (3, 10))。所有
K1
我们有相同的GroupID
在表中G
.例如,K1
1 和 2 都在组Group 1
中.
所以查询下面的 View
select K1 from GroupByK2 where K2 = 200 -- or 100
应该返回
4
5
6
因为两者都是(5, 200)
和 (6, 200)
有相同的K2
.以及 (4, 100)
的 4 和 5和 (5, 200)
都在'Group 2'
.
和select K1 from GroupByK2 where K2 = 300 -- or 310, 320, 330
应该返回 7, 8, 9, 10, 11, 12
.
查看:
create view GroupByK2
as
with cte as (
select E.*, K2 K2x from E
union all
select E.K1, E.K2, cte.K2x
from cte
join G on cte.K1 = G.K1
join G h on h.GroupID = G.GroupID
join E on E.K1 = h.K1 and E.K1 <> cte.K1
where not exists (select * from cte x where x.k1 = G.k1 and x.K2 = G.K2) -- error
)
select *
from cte;
但是SQL有错误
Recursive member of a common table expression 'cte' has multiple recursive references?
最佳答案
我有点摸不着头脑,但这是一个有效的解决方案,虽然效率很低......
您正确地尝试消除连接原始行以避免循环递归,但由于 2 个原因它不会起作用:
- 如错误所述,您不能再引用递归成员 不止一次
- 即使可以,在每次递归时,递归集只包含前一次递归的输出,所以你不会 无论如何都能够消除早期递归的循环。
我的解决方案以“不太理想”的方式避免了这种情况,它简单地包括所有带有循环的行,但将递归级别限制为硬数字(示例中为 5,但您也可以对其进行参数化)以避免无休止的递归,并且仅在最终查询时,使用 group by 消除重复项。
根据层次结构的深度,这可能对您有用或无效。它创造了大量的冗余工作,我怀疑它会扩展,但是 YMMV。我把它当作一个逻辑难题来解决:-)
这是我肯定会考虑迭代解决方案而不是基于集合的解决方案的(罕见)案例之一。您将需要创建一个表值函数,以便对其进行参数化,而您将无法使用 View 正确执行此操作。在函数内创建一个临时表或表变量,用输出集一个一个地填充它,然后循环直到完成。这样,您将能够通过检查临时表的内容并仅插入新行来消除根部的循环。
无论如何,这里是:
;WITH KeyGroups AS
(
SELECT E.*, G.GroupID
FROM E
LEFT OUTER JOIN
G
ON E.K1 = G.K1
),
Recursive AS
(
SELECT K.K1, K.K2, K.GroupID, 0 AS lvl
FROM KeyGroups AS K
WHERE K.K2 = 300
UNION ALL
SELECT K.K1, K.K2, K.GroupID, lvl + 1
FROM Recursive AS R
INNER JOIN
KeyGroups AS K
ON R.GroupID = K.GroupID
OR
R.K2 = K.K2
OR
R.K1 = K.K1
WHERE lvl < 5
)
SELECT MIN(lvl) AS lvl, K1, K2, GroupID
FROM Recursive
GROUP BY GroupID, K1, K2
ORDER BY lvl, K1, K2, GroupID;
另见 DBFiddle .
如果我明天有时间,我会再考虑一下,如果我找到更好的解决方案,我会在这里更新。
感谢您提出的有趣挑战和精心设计的帖子。
HTH
关于sql - 公用表表达式 'cte' 的递归成员有多个递归引用?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57386254/