sql - 公用表表达式 'cte' 的递归成员有多个递归引用?

标签 sql sql-server tsql

我有以下两个表EG .

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”定义:

  1. 所有 K1我们有相同的 K2在表中E .例如 E 中的 2 和 3是相关的,因为两条记录都有 K2共 10 个 ((2, 10), (3, 10))。

  2. 所有 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 个原因它不会起作用:

  1. 如错误所述,您不能再引用递归成员 不止一次
  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/

相关文章:

sql - 减少广泛的查询

sql - 在 SQL Server 2008 R2 中创建用户定义的表类型时出错

sql - 在多行中显示一列

SQL:为什么在此 where 子句中过滤掉 NULL 值?

android - 连接到网站 sql 数据库并在 android 上显示网页内容

sql - 使用来自三层嵌套表的聚合列创建 View

sql - 寻找练习来学习 SQL,使用 Northwind 数据库

sql - 从特定日期减去工作日

MySql 从内部连接更新语法错误

python - 将 Pyodbc 与 SQL Server 一起使用,正则表达式函数无法被识别