【问题标题】:SQLite Concatenating Column valuesSQLite 连接列值
【发布时间】:2015-07-17 22:09:00
【问题描述】:

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

【问题讨论】:

    标签: sqlite common-table-expression


    【解决方案1】:

    这就是group_concat() 的用途:

    SELECT Item,
           group_concat(Tag) AS Tags
    FROM (SELECT Item, Tag
          FROM T
          ORDER BY Item, Tag)
    GROUP BY Item;
    

    【讨论】:

      【解决方案2】:

      最好的解决方案(就像其他数据库一样)是使用递归 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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2023-04-08
        • 2013-09-07
        • 1970-01-01
        • 2021-02-15
        • 1970-01-01
        • 2011-08-21
        • 1970-01-01
        相关资源
        最近更新 更多