按 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 访问所有连接的节点 (name
s),避免访问现有节点。
这是一种方法:
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。
假设我们有一个 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 访问所有连接的节点 (name
s),避免访问现有节点。
这是一种方法:
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。