2013-06-25 4 views
20

Je semblent venir contre ce problème beaucoup, où j'ai données qui formatés comme ceci:"Reverse GROUP_CONCAT" dans MySQL?

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

mais je veux formaté comme ceci:

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

Y at-il une bonne façon de le faire ce? Comment s'appelle ce genre d'opération?

+1

Cette opération est appelée pivotante/unpivoting – nothrow

+0

Impressionnant, merci –

Répondre

7

Je pense qu'il est ce que vous avez besoin (procédure stockée): 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; $$ 
+0

Génial, c'est exactement ce que je cherchais –

+0

Superbe !!! Vous êtes l'homme!!! : D –

+1

@kmas, Que signifie "procédure stockée"? – Pacerier

10

Vous pouvez utiliser une requête comme ceci:

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 violon here. Veuillez noter que cette requête prendra 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 une table contenant 10 ou 100 nombres).

+0

Ce n'est pas tout à fait ce que je cherche, je cherche plus quelque chose qui peut gérer N lignes par ID. Merci cependant :) –

+0

@JasonHamje si vous avez besoin d'utiliser une requête et non une procédure stockée, il n'y a pas d'autre moyen :) – fthiella

+0

Gotcha, merci –

0

Cela m'a sauvé plusieurs heures! Pour aller plus loin: Dans une implémentation typique, il y aurait probablement une table qui énumère les couleurs par rapport à une clé d'identification, color_list. Une nouvelle couleur peut être ajoutée à la mise en œuvre sans avoir à modifier la requête et la union -clause potentiellement sans fin peut être évité en changeant tout à fait la requête à 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 important que le Ids dans le tableau color_list restent séquentiel, cependant.

0

avis cela peut se faire 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

peut également ajouter le nombre et le groupe par si vous voulez –