SQL Server 奇怪的多列分组场景和 OR

标签 sql sql-server group-by

我有一个奇怪的分组场景,并且在找出 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 OR Col2 OR Col3 相同,那么这两个记录应该在同一组中,并且应该存在链接。 换句话说,根据上述数据,记录“a”(第一条记录)的 Col3 = 3,记录“b”(第二条记录)的 Col3 = 3,因此这两条记录应该在一组中。但是记录“b”与记录“c”具有相同的 Col1,因此记录“c”应该与“a”和“b”位于同一组中。然后记录“d”具有与“c”中相同的 Col2,因此它也应该位于同一组中。同样,'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

可能有一种方法可以通过一些循环/游标来做到这一点,但我开始考虑更干净的方法,这似乎相当困难。

最佳答案

给你:

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 可以处理的内容有明显的限制。如果达到此限制,您将收到消息:

The maximum recursion 100 has been exhausted before statement completion.

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

关于SQL Server 奇怪的多列分组场景和 OR,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51632251/

相关文章:

SQL 连接一系列值(整数范围、日期范围等)

sql - 如何在同一张表中以不同条件检索同一列两次?

mysql - 为 liquibase 中的插入查询定义变更集

SQL 将现有/重复的行插入表中但只更改一列值?

php - MySQL:计算子级并获取父级行

sql - PostgreSQL,在没有可用的 ORDER BY 选项时反转递归查询

sql - Order By 条款似乎不起作用

sql-server - SQL Server Enterprise Manager 2005 - 存储过程未显示

MYSQL - 用子句计数

linq - 使用 Linq 检查列表中间是否包含空值