【问题标题】:Split query results into unique sets将查询结果拆分为唯一的集合
【发布时间】:2017-10-02 11:24:06
【问题描述】:

我有一个查询,它返回代表两个实体(直接或间接)之间关系的两列,例如,右侧图表中显示的关系将由右侧表中的数据表示:

| From | To |          1    3    4
|------|----|          o----o----o
| 1    | 3  |              / \
| 1    | 4  |             /   \
| 1    | 5  |          2 o     o 5
| 2    | 3  |                     
| 2    | 4  |
| 2    | 5  |
| 3    | 4  |          6    7
| 3    | 5  |          o----o
| 6    | 7  |

我想要做的是将这些数据分组到多个集合中,等于关系描述的不同图表的数量(因此在上面的示例中为 2 个集合)。

这种分组可以作为数据库查询 (T-SQL) 的一部分发生,也可以在数据在内存中时发生 (C#)。

【问题讨论】:

  • 我假设 1 到/从 5 在您的示例中是一个错误?或者如果不是,也应该有 4 到/从 2。
  • 谢谢尼尔,我错过了 2 到 4 的关系。我已更新问题以包含该链接。
  • 表中也不应该有 (From:1, To:2), (From:2, To:1), (From:3, to:1), (从:4,到:1)我认为如果所有关系都已完成输入表中,那么使用 CTE 导航和查找间隙(或孤岛)是可能的。如果您查看 SQL Server Internals 书籍,则可以找到寻找间隙/孤岛的解决方案。

标签: c# graph tsql


【解决方案1】:

它可能不漂亮,但这将正确地对顶点进行分组,并且只需要边作为起点。请注意,边的顶点顺序无关紧要。

-- Sample data.
declare @Edges as Table ( Vertex1 Int, Vertex2 Int );
insert into @Edges ( Vertex1, Vertex2 ) values
  ( 1, 3 ), ( 3, 4 ), ( 3, 2 ), ( 3, 5 ),
  ( 6, 7 );
select * from @Edges;

-- Create a working table that assigns each vertex to a unique "set".
declare @Sets as Table ( SetId Int, Vertex Int );
insert into @Sets ( SetId, Vertex )
  select Row_Number() over ( order by Vertex ), Vertex from (
    select distinct Vertex1 as Vertex from @Edges
    union
    select distinct Vertex2 from @Edges ) as PH;
select * from @Sets;

-- Update the working table to group vertexes into sets:
--   For each vertex update the   SetId  to the minimum   SetId  of all of the vertexes one edge away.
--   Repeat until nothing changes.
declare @UpdatedRows as Int = 42;
while @UpdatedRows > 0
  begin
  update NS
    set SetId = MinSetId
    from (
      select S.SetId, S.Vertex,
        ( select Min( SetId ) from @Sets where Vertex in (
          select S.Vertex union
          select Vertex1 from @Edges where Vertex2 = S.Vertex union
          select Vertex2 from @Edges where Vertex1 = S.Vertex )
          ) as MinSetId
        from @Sets as S ) as NS
    where SetId != MinSetId;
  set @UpdatedRows = @@RowCount;
  select * from @Sets;  -- Show the work as it progresses.
  end

  -- The   SetId   values can be collapsed using   Dense_Rank .
  select Dense_Rank() over ( order by SetId ) as SetId, Vertex
    from @Sets;

【讨论】:

  • 我最终在数据访问代码 (C#) 中以类似的方式解决了这个问题,但感谢@HABO 对 SQL 的关注!
猜你喜欢
  • 1970-01-01
  • 2011-12-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-02-04
  • 2013-07-25
相关资源
最近更新 更多