web-dev-qa-db-fra.com

Fractionner une chaîne et parcourir les valeurs dans la procédure MySql

J'ai eu une situation où je dois passer une chaîne séparée par des virgules à la procédure MySQL et diviser cette chaîne et insérer ces valeurs sous forme de lignes dans une table.

Comme le montrent ci-dessous

Par exemple, si j'ai passé la chaîne 'jhon, swetha, sitha' à la procédure mysql, il doit diviser cette chaîne par une virgule et insérer ces valeurs sous forme de 3 enregistrements dans une table.

  CREATE PROCEDURE new_routine (IN str varchar(30))   
   BEGIN
       DECLARE tmp varchar(10);
       DECLARE inc INT DEFAULT 0; 
       WHILE INSTR(str, ',') DO
         SET tmp = SUBSTRING(SUBSTRING_INDEX(str,',',inc),LENGTH(SUBSTRING_INDEX(str,',',inc-1))+1),',','');
         SET str = REPLACE(str, tmp, '');
         //insert tmp into a table.
       END WHILE;
    END

Mais cela n'a pas fonctionné s'il vous plaît.

Vous devrez être un peu plus prudent avec votre manipulation de chaîne. Vous ne pouvez pas utiliser REPLACE() pour cela, car cela remplacera plusieurs occurrences, corrompant vos données si un élément de la liste séparée par des virgules est une sous-chaîne d'un autre élément. La fonction INSERT() chaîne est préférable pour cela, à ne pas confondre avec l'instruction INSERT utilisée pour l'insertion dans une table.

DELIMITER $$

DROP PROCEDURE IF EXISTS `insert_csv` $$
CREATE PROCEDURE `insert_csv`(_list MEDIUMTEXT)
BEGIN

DECLARE _next TEXT DEFAULT NULL;
DECLARE _nextlen INT DEFAULT NULL;
DECLARE _value TEXT DEFAULT NULL;

iterator:
LOOP
  -- exit the loop if the list seems empty or was null;
  -- this extra caution is necessary to avoid an endless loop in the proc.
  IF LENGTH(TRIM(_list)) = 0 OR _list IS NULL THEN
    LEAVE iterator;
  END IF;

  -- capture the next value from the list
  SET _next = SUBSTRING_INDEX(_list,',',1);

  -- save the length of the captured value; we will need to remove this
  -- many characters + 1 from the beginning of the string 
  -- before the next iteration
  SET _nextlen = LENGTH(_next);

  -- trim the value of leading and trailing spaces, in case of sloppy CSV strings
  SET _value = TRIM(_next);

  -- insert the extracted value into the target table
  INSERT INTO t1 (c1) VALUES (_value);

  -- rewrite the original string using the `INSERT()` string function,
  -- args are original string, start position, how many characters to remove, 
  -- and what to "insert" in their place (in this case, we "insert"
  -- an empty string, which removes _nextlen + 1 characters)
  SET _list = INSERT(_list,1,_nextlen + 1,'');
END LOOP;

END $$

DELIMITER ;

Ensuite, un tableau pour tester:

CREATE TABLE `t1` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `c1` varchar(64) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

La nouvelle table est vide.

mysql> SELECT * FROM t1;
Empty set (0.00 sec)

Appelez la procédure.

mysql> CALL insert_csv('foo,bar,buzz,fizz');
Query OK, 1 row affected (0.00 sec)

Notez que "1 ligne affectée" ne signifie pas ce à quoi vous vous attendez. Cela fait référence au dernier encart que nous avons fait. Puisque nous insérons une ligne à la fois, si la procédure insère au moins une ligne, vous obtiendrez toujours un nombre de lignes de 1; si la procédure n'insère rien, vous obtiendrez 0 lignes affectées.

Cela a-t-il fonctionné?

mysql> SELECT * FROM t1;
+----+------+
| id | c1   |
+----+------+
|  1 | foo  |
|  2 | bar  |
|  3 | buzz |
|  4 | fizz |
+----+------+
4 rows in set (0.00 sec)
39
Michael - sqlbot