最好的解决方案(就像其他数据库一样)是使用递归 CTE 将表连接回自身。
解决方案
如果你只是想要解决方案,那么这里是最终的 SQL
with recursive
complete as
(select Item, Tag, (select count(*) from T b where a.Tag > b.Tag and b.Item = a.Item) as cnt
from T a
),
summary(item, tags, cnt) as
(
-- Select the initial seed values
select item, tag, cnt
from complete
where cnt = 0
union all
-- Concatenate the next rows values onto the previous rows (this is the recursive part)
select a.item, a.tags || ',' || b.tag, b.cnt
from summary a
join complete b on a.item = b.item and a.cnt + 1 = b.cnt
-- limit 200
)
final( ItemID, tags, cnt) as
(
select ItemID, tags, max(cnt) from summary -- limit the selected values to the final rows concatenated values
group by ItemID
)
select ItemID, tags from final
;
解释
如果您想了解它的工作原理,请继续阅读
首先创建一个测试表
--drop table T;
create table T (Item, Tag);
insert into T values('Item1', 'A');
insert into T values('Item1', 'B');
insert into T values('Item1', 'C');
insert into T values('Item2', 'A');
insert into T values('Item1', 'D');
insert into T values('Item2', 'F');
insert into T values('Item1', 'E');
select * from T;
这给了你平桌
Item Tag
"Item1" "A"
"Item1" "B"
"Item1" "C"
"Item2" "A"
"Item1" "D"
"Item2" "F"
"Item1" "E"
然后为项目标签分配增量编号
select Item,
Tag,
(select count(*) from T b where a.Tag > b.Tag and b.Item = a.Item) as cnt
from T a order by 1,3; -- note we don't need order later on
这将给出一个结果集
Item Tag cnt
"Item1" "A" "0"
"Item1" "B" "1"
"Item1" "C" "2"
"Item1" "D" "3"
"Item1" "E" "4"
"Item2" "A" "0"
"Item2" "F" "1"
然后是递归 CTE
我们开始
with recursive
说我们正在做 CTE
然后分配一个包含所有所需数据的表 - 这里称为完整
complete as
(select Item, Tag, (select count(*) from T b where a.Tag > b.Tag and b.Item = a.Item) as cnt
from T a
),
然后定义表和列必须为我们之前创建的行号有一个占位符 - 这里的表称为summary,行号是cnt。标签用于使正在发生的事情更加明显
summary(item, tags, cnt) as
(
然后选择初始种子值。在这种情况下,这是每个项目的第一行。
-- Select the initial seed values
select item, tag, cnt
from complete
where cnt = 0
然后我们union all回到当前表,注意你可以使用union但是推荐使用all,因为它不检查重复,因此更快
union all
-- Concatenate the next rows values onto the previous rows (this is the recursive part)
select a.item, a.tags || ',' || b.tag, b.cnt
from summary a
join complete b on a.item = b.item and a.cnt + 1 = b.cnt
-- limit 200
),
请注意,在摘要的定义中,它实际上连接回摘要。这就是为什么它是一个递归 CTE - 以及为什么在创建它们时必须小心
然后通过仅选择每个项目的最大行数来删除我们迭代的额外行以获得最终行的连接值
final( Item, tags, cnt) as
(
select Item, tags, max(cnt) from summary -- limit the selected values to the final rows concatenated values
group by Item
)
然后从最终切割表中选择值
select Item, tags from final
;
这给出了所需的汇总数据
Item Tags
"Item1" "A,B,C,D,E"
"Item2" "A,F"
请注意,我最初没有决赛桌,但只有代码
select item, tags, max(cnt) from summary
group by item
having max(cnt) -- limit the selected values to the final rows concatenated values
这适用于我给出的具体示例,但不适用于我实际使用的略有不同的东西,这也是我添加决赛桌的原因。
注意
- 关键字 recursive 是标准但不是必需的
- 在开发递归 CTE 时使用 LIMIT 功能通常是一个好主意,这样如果你把它塞进去就不会崩溃——它目前在上面的代码中被注释掉了。
有关如何使用 SQLite 进行递归 CTE 的更多信息,请参阅 SQLite WITH explanation