代码之家  ›  专栏  ›  技术社区  ›  Ihor Deyneka

SQL Server按多个列和/或对场景进行奇怪分组

  •  9
  • Ihor Deyneka  · 技术社区  · 6 年前

    我有一个奇怪的分组场景,在发现什么是SQL中分组的最佳方式时遇到了一些困难。

    假设我们有一张桌子

    CREATE TABLE Item
    (
      KeyId VARCHAR(1) NOT NULL,
      Col1 INT NULL,
      Col2 INT NULL,
      Col3 INT NULL
    )
    
    GO
    
    INSERT INTO Item (KeyId, Col1, Col2, Col3)
    VALUES 
    ('a',1,2,3),
    ('b',5,4,3),
    ('c',5,7,6),
    ('d',8,7,9),
    ('e',11,10,9),
    ('f',11,12,13),
    ('g',20,22,21),
    ('h',23,22,24)
    

    我需要对这个表中的记录进行分组,以便如果两个记录的Col1、Col2或Col3相同,那么这两个记录应该在同一个组中,并且应该有链接。 换句话说,对于上述数据,记录“a”(第一个记录)具有Col3=3,记录“b”(第二个记录)也具有Col3=3,因此这两个记录应该在一个组中。但是记录“b”与记录“c”具有相同的Col1,因此记录“c”应与“a”和“b”位于同一组中。然后记录'd'的Col2与'c'中的相同,所以它也应该在同一组中。类似地,“e”和“f”分别在Col3和Col1中具有相同的值。

    另一方面,记录“g”和“h”将在一个组中(因为它们具有相同的Col2=22),但是这个组与记录“a”、“b”、“c”、“d”、“e”、“f”的组不同。

    查询的结果应该类似于

    KeyId GroupId
    'a'   1 
    'b'   1
    'c'   1
    'd'   1
    'e'   1
    'f'   1
    'g'   2
    'h'   2
    

    可能有一种方法可以使用一些循环/游标来实现这一点,但是我开始考虑使用更干净的方法,这看起来相当困难。

    1 回复  |  直到 6 年前
        1
  •  4
  •   The Impaler    6 年前

    干得好:

    with g (rootid, previd, level, keyid, col1, col2, col3) as (
      select keyid, '-', 1, keyid, col1, col2, col3 from item
      union all
      select g.rootid, g.keyid, g.level + 1, i.keyid, i.col1, i.col2, i.col3 
        from g
        join item i on i.col1 = g.col1 or i.col2 = g.col2 or i.col3 = g.col3 
        where i.keyid > g.keyid
    ),
      m (keyid, rootid) as (
      select keyid, min(rootid) from g group by keyid
    )
    select * from m;
    

    结果:

    keyid  rootid  
    -----  ------
    a      a       
    b      a       
    c      a       
    d      a       
    e      a       
    f      a       
    g      g       
    h      g       
    

    注意 :请记住,在处理递归CTE时,SQL Server的默认迭代次数限制为100次(每组行数)。 英语 :尽管可以如上所示执行此操作,但SQL Server可以处理的内容有明显的限制。如果达到此限制,您将收到消息:

    最大递归100在语句完成之前已经用完。

    如果发生这种情况,请考虑添加子句 option (maxrecursion 32767) .