MySQL 中 GROUP_CONCAT 的对立面是啥?

Posted

技术标签:

【中文标题】MySQL 中 GROUP_CONCAT 的对立面是啥?【英文标题】:What is the opposite of GROUP_CONCAT in MySQL?MySQL 中 GROUP_CONCAT 的对立面是什么? 【发布时间】:2013-06-22 22:03:40 【问题描述】:

我似乎经常遇到这个问题,我的数据格式如下:

+----+----------------------+
| id | colors               |
+----+----------------------+
| 1  | Red,Green,Blue       |
| 2  | Orangered,Periwinkle |
+----+----------------------+

但我希望它的格式如下:

+----+------------+
| id | colors     |
+----+------------+
| 1  | Red        |
| 1  | Green      |
| 1  | Blue       |
| 2  | Orangered  |
| 2  | Periwinkle |
+----+------------+

有什么好办法吗?这种操作到底叫什么?

【问题讨论】:

那个操作叫做pivoting / unpivoting 您可以使用 FIND_IN_SET dev.mysql.com/doc/refman/8.0/en/… 您也可以在 JOIN 中组合。 【参考方案1】:

您可以使用这样的查询:

SELECT
  id,
  SUBSTRING_INDEX(SUBSTRING_INDEX(colors, ',', n.digit+1), ',', -1) color
FROM
  colors
  INNER JOIN
  (SELECT 0 digit UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3) n
  ON LENGTH(REPLACE(colors, ',' , '')) <= LENGTH(colors)-n.digit
ORDER BY
  id,
  n.digit

请看小提琴here。请注意,此查询每行最多支持 4 种颜色,您应该更新您的子查询以返回 4 个以上的数字(或者您应该使用包含 10 或 100 个数字的表)。

【讨论】:

这不是我要找的东西,我更多的是寻找可以处理每个 id 的 N 行的东西。不过谢谢:) @JasonHamje 如果您需要使用查询而不是存储过程,没有其他方法:) 非常感谢。用于Here(Edit2 块)并给出归属:p @Drew 不客气!感谢您的归属! ;) 不错的答案。在一般情况下,如果结合this answer 中的技术生成长数字序列,此方法非常强大。【参考方案2】:

我认为这是您所需要的(存储过程):Mysql split column string into rows

DELIMITER $$

DROP PROCEDURE IF EXISTS explode_table $$
CREATE PROCEDURE explode_table(bound VARCHAR(255))

BEGIN

DECLARE id INT DEFAULT 0;
DECLARE value TEXT;
DECLARE occurance INT DEFAULT 0;
DECLARE i INT DEFAULT 0;
DECLARE splitted_value INT;
DECLARE done INT DEFAULT 0;
DECLARE cur1 CURSOR FOR SELECT table1.id, table1.value
                                     FROM table1
                                     WHERE table1.value != '';
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;

DROP TEMPORARY TABLE IF EXISTS table2;
CREATE TEMPORARY TABLE table2(
`id` INT NOT NULL,
`value` VARCHAR(255) NOT NULL
) ENGINE=Memory;

OPEN cur1;
  read_loop: LOOP
    FETCH cur1 INTO id, value;
    IF done THEN
      LEAVE read_loop;
    END IF;

    SET occurance = (SELECT LENGTH(value)
                             - LENGTH(REPLACE(value, bound, ''))
                             +1);
    SET i=1;
    WHILE i <= occurance DO
      SET splitted_value =
      (SELECT REPLACE(SUBSTRING(SUBSTRING_INDEX(value, bound, i),
      LENGTH(SUBSTRING_INDEX(value, bound, i - 1)) + 1), ',', ''));

      INSERT INTO table2 VALUES (id, splitted_value);
      SET i = i + 1;

    END WHILE;
  END LOOP;

  SELECT * FROM table2;
 CLOSE cur1;
 END; $$

【讨论】:

太棒了,这正是我想要的 @kmas,“库存程序”是什么意思? bound 参数有什么作用? (编辑):看起来它充当要替换的分隔符。 REPLACE(str, find_string, replace_with) => REPLACE(value, bound, '')【参考方案3】:

这为我节省了很多时间!更进一步:在一个典型的实现中,很可能会有一个表,它根据标识键 color_list 枚举颜色。无需修改查询即可将新颜色添加到实现中,并且可以通过将查询更改为以下内容来完全避免可能无穷无尽的union -clause:

SELECT id,
  SUBSTRING_INDEX(SUBSTRING_INDEX(colors, ',', n.digit+1), ',', -1) color
FROM
  colors
  INNER JOIN
  (select id as digit from color_list) n
  ON LENGTH(REPLACE(colors, ',' , '')) <= LENGTH(colors)-n.digit
ORDER BY id, n.digit;

但是,表 color_list 中的 Id 保持顺序很重要。

【讨论】:

【参考方案4】:

不需要存储过程。一个 CTE 就足够了:

CREATE TABLE colors(id INT,colors TEXT);
INSERT INTO colors VALUES (1, 'Red,Green,Blue'), (2, 'Orangered,Periwinkle');

WITH RECURSIVE
  unwound AS (
    SELECT *
      FROM colors
    UNION ALL
    SELECT id, regexp_replace(colors, '^[^,]*,', '') colors
      FROM unwound
      WHERE colors LIKE '%,%'
  )
  SELECT id, regexp_replace(colors, ',.*', '') colors
    FROM unwound
    ORDER BY id
;
+------+------------+
| id   | colors     |
+------+------------+
|    1 | Red        |
|    1 | Green      |
|    1 | Blue       |
|    2 | Orangered  |
|    2 | Periwinkle |
+------+------------+

【讨论】:

如果这个存在于 2013 年就好了!非常酷。我不再经常使用 MySQL,但如果我这样做了,我一定会记得检查一下。 @JasonHamje 这不是 MySQL/MariaDB 特定的。相同的代码适用于 PostgreSQL。如果加载一个扩展来添加函数regexp_replace,它也可以在SQLite上运行。【参考方案5】:

注意这可以在不创建临时表的情况下完成

select id, substring_index(substring_index(genre, ',', n), ',', -1) as genre
from my_table
join 
(SELECT @row := @row + 1 as n FROM 
(select 0 union all select 1 union all select 3 union all select 4 union all select 5 union all select 6 union all select 6 union all select 7 union all select 8 union all select 9) t,
(SELECT @row:=0) r) as numbers
  on char_length(genre) 
    - char_length(replace(genre, ',', ''))  >= n - 1

【讨论】:

如果你愿意,也可以添加计数和分组 在 select 语句中读取和写入相同的用户变量是未定义的行为。请参阅 MySQL 手册重新用户变量和分配。【参考方案6】:

如果分隔符是数据的一部分,但被双引号嵌入,那么我们如何分割它。

示例 第一个,“第二个,s”,第三个

它应该是 第一的 第二,s 第三个

【讨论】:

看这个有点晚了..但是为什么不使用替换删除引号然后按照答案说的做呢?

以上是关于MySQL 中 GROUP_CONCAT 的对立面是啥?的主要内容,如果未能解决你的问题,请参考以下文章

MySQL 的对立面轻量级之 MongoDB 数据库

MySQL中使用group_concat遇到的坑

MySQL中GROUP_CONCAT中排序

MYSQL中group_concat有长度限制!默认1024

MYSQL中group_concat有长度限制!默认1024(转载)

mysql中group_concat函数的作用