web-dev-qa-db-fra.com

MySQL scinde la chaîne par des virgules dans la table temporaire

Pouvez-vous analyser une chaîne séparée par des virgules dans une table temporaire dans MySQL en utilisant RegEx?

'1|2|5|6' into temp table with 4 rows.
23
Mike Flynn

C’est à peu près la même question que Mysql peut-il fractionner une colonne?

MySQL n'a pas de fonction split string, vous devez donc contourner le problème. Vous pouvez faire n'importe quoi avec les données une fois que vous les avez divisées en utilisant l’une des méthodes énumérées dans la page de réponses ci-dessus.

Vous pouvez faire une boucle sur cette fonction personnalisée et casser quand elle retourne vide, vous devrez jouer et apprendre une syntaxe (ou du moins je le ferais) mais la syntaxe d'une boucle FOR dans mysql est la suivante: http: //www.roseindia.net/sql/mysql-example/for.shtml

Vous pouvez le parcourir en incrémentant la position dans la fonction ci-dessous:

CREATE FUNCTION SPLIT_STR(
  x VARCHAR(255),
  delim VARCHAR(12),
  pos INT
)
RETURNS VARCHAR(255)
RETURN REPLACE(SUBSTRING(SUBSTRING_INDEX(x, delim, pos),
       LENGTH(SUBSTRING_INDEX(x, delim, pos -1)) + 1),
       delim, '');

(Crédit: https://blog.fedecarg.com/2009/02/22/mysql-split-string-function/ )

Ce qui devrait retourner '' si aucune correspondance n'est trouvée, alors rompez la boucle si aucune correspondance n'est trouvée. Cela vous permettra d'analyser uniquement avec mysql sur la chaîne divisée et d'exécuter les requêtes d'insertion dans une table temporaire. Mais pourquoi ne pas utiliser un langage de script tel que php pour ce genre de travail? :(

Code pour la syntaxe de la boucle:

DELIMITER $$  

CREATE PROCEDURE ABC(fullstr)

   BEGIN
      DECLARE a INT Default 0 ;
      DECLARE str VARCHAR(255);
      simple_loop: LOOP
         SET a=a+1;
         SET str=SPLIT_STR(fullstr,"|",a);
         IF str='' THEN
            LEAVE simple_loop;
         END IF;
         #Do Inserts into temp table here with str going into the row
         insert into my_temp_table values (str);
   END LOOP simple_loop;
END $$
33
Wolfe

J'ai fait ceci, pour quand vous n'avez pas de valeurs de table et ainsi de suite:

select *
from(
    select c, SUBSTRING_INDEX(SUBSTRING_INDEX('1|2|5|6', '|', c+1), '|', -1) as name
    from(
        SELECT (TWO_1.SeqValue + TWO_2.SeqValue + TWO_4.SeqValue + TWO_8.SeqValue + TWO_16.SeqValue + TWO_32.SeqValue) c
        FROM (
            SELECT 0 SeqValue UNION ALL SELECT 1 SeqValue) TWO_1
            CROSS JOIN (SELECT 0 SeqValue UNION ALL SELECT 2 SeqValue) TWO_2
            CROSS JOIN (SELECT 0 SeqValue UNION ALL SELECT 4 SeqValue) TWO_4
            CROSS JOIN (SELECT 0 SeqValue UNION ALL SELECT 8 SeqValue) TWO_8
            CROSS JOIN (SELECT 0 SeqValue UNION ALL SELECT 16 SeqValue) TWO_16 
            CROSS JOIN (SELECT 0 SeqValue UNION ALL SELECT 32 SeqValue) TWO_32
    ) as b
    WHERE c <= (CHAR_LENGTH('1|2|5|6') - CHAR_LENGTH(REPLACE('1|2|5|6', '|', '')))
) as a;

Peut-être pas la meilleure réponse, mais fonctionne sans aide de fonctions et procédures, pas de tables supplémentaires, etc.

3
Rui Costa

Vous pouvez utiliser une expression régulière dans MySQL pour spécifier un modèle pour une recherche complexe. Vous ne pouvez pas analyser les chaînes.

Mais vous pouvez créer une requête INSERT à l'aide de REPLACE et CONCATENATE pour enregistrer des données dans une table temporaire.

2
Wasif
DELIMITER $$  

CREATE PROCEDURE SPLIT_VALUE_STRING()

    BEGIN

        SET @String      = '1,22,333,444,5555,66666,777777';
        SET @Occurrences = LENGTH(@String) - LENGTH(REPLACE(@String, ',', ''));
        myloop: WHILE (@Occurrences > 0)
        DO 
            SET @myValue = SUBSTRING_INDEX(@String, ',', 1);
            IF (@myValue != '') THEN
            /* my code... */
            ELSE
                LEAVE myloop; 
            END IF;
            SET @Occurrences = LENGTH(@String) - LENGTH(REPLACE(@String, ',', ''));
            IF (@occurrences = 0) THEN 
                LEAVE myloop; 
            END IF;
            SET @String = SUBSTRING(@String,LENGTH(SUBSTRING_INDEX(@String, ',', 1))+2);
        END WHILE;                  

   END $$
2
Guzzo Walter
select distinct
  SUBSTRING_INDEX(SUBSTRING_INDEX('1,2,3,4', ',', numbers.n), ',', -1) name
from
  (select @rownum := @rownum + 1 as n
from YourTable
cross join (select @rownum := 0) r
) numbers 
order by
   n
1
Sunil

J'ai trouvé une bonne solution pour cela

https://forums.mysql.com/read.php?10,635524,635529

Merci à Peter Brawley

Astuce: convertissez un résultat Group_Concat () sur la chaîne csv en un Insérer ... valeurs ... chaîne:

drop table if exists t;
create table t( txt text );
insert into t values('1,2,3,4,5,6,7,8,9');

drop temporary table if exists temp;
create temporary table temp( val char(255) );
set @sql = concat("insert into temp (val) values ('", replace(( select group_concat(distinct txt) as data from t), ",", "'),('"),"');");
prepare stmt1 from @sql;
execute stmt1;
select distinct(val) from temp;
+------+
| val  |
+------+
| 1    |
| 2    |
| 3    |
| 4    |
| 5    |
| 6    |
| 7    |
| 8    |
| 9    |
+------+

Aussi, si vous voulez juste joindre une table à une liste d'identifiant, vous pouvez utiliser l'opérateur LIKE . rejoindre des produits connexes par liste d'identifiant avec l'opérateur LIKE.

SELECT b2.id blog_id, b2.id_list, p.id
FROM (
    SELECT b.id,b.text,
    CONCAT(
        ",",
            REPLACE(
                EXTRACTVALUE(b.text,'//a/@id')
                , " ", ","
            )
        ,","
    ) AS id_list
    FROM blog b
) b2
LEFT JOIN production p ON b2.id_list LIKE CONCAT('%,',p.id,',%')
HAVING b2.id_list != ''
1
Alexey Muravyov

Juste parce que j'aime vraiment ressusciter de vieilles questions:

CREATE PROCEDURE `SPLIT_LIST_STR`(IN `INISTR` TEXT CHARSET utf8mb4, IN `ENDSTR` TEXT CHARSET utf8mb4, IN `INPUTSTR` TEXT CHARSET utf8mb4, IN `SEPARATR` TEXT CHARSET utf8mb4)
BEGIN
    SET @I = 1;
    SET @SEP = SEPARATR;
    SET @INI = INISTR;
    SET @END = ENDSTR;
    SET @VARSTR = REPLACE(REPLACE(INPUTSTR, @INI, ''), @END, '');
    SET @N = FORMAT((LENGTH(@VARSTR)-LENGTH(REPLACE(@VARSTR, @SEP, '')))/LENGTH(@SEP), 0)+1;

    CREATE TEMPORARY TABLE IF NOT EXISTS temp_table(P1 TEXT NULL);

    label1: LOOP
        SET @TEMP = SUBSTRING_INDEX(@VARSTR, @SEP, 1);
        insert into temp_table (`P1`) SELECT @TEMP;
        SET @I = @I + 1;
        SET @VARSTR = REPLACE(@VARSTR, CONCAT(@TEMP, @SEP), '');
        IF @N >= @I THEN
          ITERATE label1;
        END IF;
        LEAVE label1;
      END LOOP label1;
    SELECT * FROM temp_table;
    END

Qui produit:

P1
1
2
3
4

Lors de l'utilisation de CALL SPLIT_LIST_STR('("', '")', '("1", "2", "3", "4")', '", "');

Je pourrais apparaître plus tard pour mettre le code un peu plus loin! À votre santé!

0

Si le texte que vous essayez de scinder contient des caractères multi-octets, cette approche échouera à cause du calcul incorrect de LENGTH. Dans ce cas, la version suivante avec CHAR_LENGTH au lieu de LENGTH fonctionne:

CREATE DEFINER=`root`@`localhost` FUNCTION `strSplit`(
           `src` MEDIUMTEXT CHARACTER SET utf8, 
           `delim` VARCHAR(12), 
           `pos` INTEGER
          )
    RETURNS mediumtext
    LANGUAGE SQL
    NOT DETERMINISTIC
    CONTAINS SQL
    SQL SECURITY DEFINER
    COMMENT ''
BEGIN
  DECLARE output MEDIUMTEXT CHARACTER SET utf8;
  SET output = REPLACE(SUBSTRING(SUBSTRING_INDEX(src, delim, pos) ,  
              CHAR_LENGTH(SUBSTRING_INDEX(src, delim, pos - 1)) + 1) , delim , '');
  IF output = '' THEN SET output = null; END IF;
  RETURN output;
END

Référence: http://www.shakedos.com/2011/Nov/23/mysql-split-string-function-fix-split_str.html

0
sprockets