其他数据库类型也有类似的答案,但我没有为SQLite找到这样的例子,所以我已经包含了我想出的答案。
问题
给出如下表格
╔══════════╦══════╗
║ Item ║ Tag ║
╠══════════╬══════╣
║ "Item1" ║ "A" ║
║ "Item1" ║ "B" ║
║ "Item1" ║ "C" ║
║ "Item2" ║ "A" ║
║ "Item1" ║ "D" ║
║ "Item2" ║ "F" ║
║ "Item1" ║ "E" ║
╚══════════╩══════╝
创建输出:
╔═══════════╦═════════════╗
║ Item ║ Tags ║
╠═══════════╬═════════════╣
║ "Item1" ║ "A,B,C,D,E" ║
║ "Item2" ║ "A,F" ║
╚═══════════╩═════════════╝
答案 0 :(得分:3)
这就是group_concat()的用途:
SELECT Item,
group_concat(Tag) AS Tags
FROM (SELECT Item, Tag
FROM T
ORDER BY Item, Tag)
GROUP BY Item;
答案 1 :(得分:0)
最好的解决方案(就像其他数据库一样)是使用递归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,rownumber是cnt。标签用于使发生的事情变得更加明显
summary(item, tags, cnt) as
(
然后选择初始播种值。在这种情况下,这是每个项目的第一行。
-- Select the initial seed values
select item, tag, cnt
from complete
where cnt = 0
然后我们联合所有回到当前表格,注意您可以使用 union 但是建议使用所有它不检查重复项,因此更快
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
这适用于我给出的具体示例,但不是我实际使用的略有不同的东西,这就是为什么我也添加了最终表。
注意的
- 关键字递归是标准但不是必需的
- 在开发递归CTE时使用LIMIT功能通常是个好主意,这样如果你填充它就不会崩溃 - 它目前在上面的代码中被注释掉了。 < / p>
有关如何使用SQLite进行递归CTE的更多信息,请参阅SQLite WITH explanation