按 2 列或与另一列的相似性对项目进行分组

Group items by 2 columns or similarity with another column

假设我们有一个 Sqlite table 包含:

name;city;age;id;foo
Alice;New-York;25;13782749;12
Eve;Chicago;23;1938679;34
Bob;New-York;25;824697624;56
Jack;Denver;30;239679163;12
Simone;Denver;30;1687631;99

如何按 h=CONCAT(city,age) 以及 在组中添加列 foo 与某人相同的用户小组?

所示,这里是如何按 h 分组:

select dense_rank() over (order by city, age) as grpnum, name, id from t;

如何在 foo 上添加第二个条件?

示例:Alice 和 Bob 在同一个 group1 中,因为他们具有相同的 h=CONCAT(city,age),但 Jack 应该 group1 因为他和 Alice 有相同的 foo 值:12。 Simone 与 Jack 的 CONCAT(city,age) 相同,因此她与 Jack 在同一组中,也在 group1 中。

这要棘手得多。这是一个图遍历问题,需要递归 CTE (Common Table Expression)。

想法是生成名称之间的所有边。然后使用递归 CTE 访问所有连接的节点 (names),避免访问现有节点。

这是一种方法:

with recursive edges as (
      select distinct t1.name as name1, t2.name as name2
      from t t1 join
           t t2
           on t1.city = t2.city and t1.age = t2.age or
              t1.foo = t2.foo
     ),
     cte as (
      select name1, name2, min(name1, name2) as first_name,
             ',' || name1 || ',' || name2 || ',' as visited
      from edges
      union all
      select cte.name1, e.name2, min(cte.first_name, e.name2),
             visited || e.name2 || ','
      from cte join
           edges e
           on e.name1 = cte.name2
      where visited not like '%,' || e.name2 || ',%'
     )
select t.*, cte.grpnum
from t join
     (select name1, min(first_name), dense_rank() over (order by min(first_name)) as grpnum
      from cte
      group by name1
     ) cte
     on t.name = cte.name1;

here是一个db<>fiddle。