通过T-SQL找到基于传递性的所有可能的对

时间:2017-03-02 12:39:19

标签: sql sql-server

我有一个问题,不让我已经好几天了。 集体思想是我可以依赖的最后手段。

假设我们有一个包含两列的表。实际上,值是GUID,但为了简单起见,我们将它们作为字母。

| a | b |
|---|---|
| x | y |
| y | x |
| y | z |
| z | y |
| m | n |
| m | z |

我需要创建一个T-SQL查询,它将从trasitivity中提出所有可能的对,即如果x = y,y = z,则x = z。此外,必须存在simmetry,即如果有x = y,那么也应该有y = x。 在这种特殊情况下,我认为存在“满屋”,这意味着每个字母都通过中间体与所有其他字母相连。但我需要一个可以显示的查询。 我所做的只是在这里(SQLFiddle无法运行它):

    WITH
    t AS
      (SELECT 'x' AS a, 'y' AS b
       UNION ALL
       SELECT 'y' AS a, 'x' AS b
       UNION ALL 
       SELECT 'y' AS a, 'z' AS b
       UNION ALL 
       SELECT 'z' AS a, 'y' AS b
       UNION ALL 
       SELECT 'm' AS a, 'n' AS b
       UNION ALL 
       SELECT 'm' AS a, 'z' AS b),

    coupled_reflective AS --for reflective couples we take either of them

      (SELECT t2.a, t2.b
       FROM t t1
       JOIN t t2 ON t1.a=t2.b
       AND t1.b!=t2.a),

    reversive_coupled_reflective AS --that's another half of the above couples (reversed)

      (SELECT t2.b, t2.a
       FROM t t1
       JOIN t t2 ON t1.a=t2.b
       AND t1.b!=t2.a),

    rs AS -- reduce the initial set (t)

      (SELECT *
       FROM coupled_reflective
       UNION
       SELECT *
       FROM t
       EXCEPT
       SELECT *
       FROM reversive_coupled_reflective),

     cte AS -- recursively iterate through the set to find transitive values (get linked by the left field)

      (SELECT a, b
       FROM rs
       UNION ALL
       SELECT rs.b, cte.b
       FROM rs
       JOIN cte ON rs.a=cte.a
       AND rs.b!=cte.b),

     cte2 AS -- recursively iterate through the set to find transitive values (get linked by the right field)

      (SELECT a, b
       FROM rs
       UNION ALL
       SELECT rs.a, cte.a
       FROM rs
       JOIN cte ON rs.b=cte.b
       AND rs.a!=cte.a)



    SELECT a, b FROM cte2
    UNION
    SELECT a, b FROM cte
    UNION
    SELECT a, b FROM t
    UNION
    SELECT b, a FROM t

但不幸的是,这并没有成功。

期望的结果应该是

 | a | b |
 |---|---|
 | x | y |
 | y | x |
 | y | z |
 | z | y |
 | m | n |
 | m | z |
 | n | m |
 | z | m |
 | x | z |
 | z | x |
 | x | m |
 | m | x |
 | x | n |
 | n | x |
 | y | m |
 | m | y |
 | y | n |
 | n | y |

那里有一个有天赋的SQL好友可以帮助我吗? 感谢。

1 个答案:

答案 0 :(得分:3)

您可以使用递归CTE,但需要已访问过的节点列表。您可以使用字符串实现它:

with cte as (
      select a, b, cast('{' + a + '}{' + b + '}' as varchar(max)) as visited
      from t
      union all
      select cte.a, t.b,
             (visited + '{' + t.b + '}')
      from cte join
           t
           on cte.b = t.a
      where cte.visited not like '%{' + t.b + '}%'
     )
select distinct a, b
from cte;

注意:

以上是图表中定向的链接。如果您想要无向链接,请同时包含:

with t as (
      select a, b from yourtable
      union
      select b, a from yourtable
     ),

其余逻辑遵循t