web-dev-qa-db-fra.com

Quel est l'opposé de GROUP_CONCAT dans MySQL?

Il semble que je me heurte souvent à ce problème, car j'ai des données formatées comme ceci:

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

mais je le veux formaté comme ceci:

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

Y at-il un bon moyen de le faire? Comment appelle-t-on ce genre d'opération?

24
Jason Hamje

Je pense que c'est ce dont vous avez besoin (procédure stockée): Mysql divise une chaîne de colonne en lignes

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; $$
9
kmas

Vous pouvez utiliser une requête comme celle-ci:

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

S'il vous plaît voir le violon ici . Notez que cette requête prend en charge jusqu'à 4 couleurs pour chaque ligne. Vous devez mettre à jour votre sous-requête pour renvoyer plus de 4 nombres (ou vous devez utiliser un tableau contenant 10 ou 100 nombres).

16
fthiella

si le délimiteur fait partie des données mais est entouré de guillemets, comment le scinder?.

Exemple Premier, "deuxième, s", troisième

il devrait venir comme premier deuxième, s troisième

0
sailesh

Cela m'a sauvé de nombreuses heures! Pour aller encore plus loin: sur une implémentation typique, il y aurait très probablement un tableau énumérant les couleurs par rapport à une clé d'identification, color_list. Une nouvelle couleur peut être ajoutée à l'implémentation sans avoir à modifier la requête et la clause union potentiellement illimitée peut être totalement évitée en remplaçant la requête par ceci:

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;

Il est toutefois important que les identifiants de la table color_list restent séquentiels.

0
gerrit_hoekstra

remarquez que cela peut être fait sans créer une table temporaire

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
0
yael alfasi