SQLite连接列值

时间:2015-07-17 22:09:00

标签: sqlite common-table-expression

其他数据库类型也有类似的答案,但我没有为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"       ║
╚═══════════╩═════════════╝

2 个答案:

答案 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