【问题标题】:MySql, split a string and insert into tableMySql,拆分字符串并插入表
【发布时间】:2011-10-21 06:27:29
【问题描述】:

我的存储过程有两个输入。一个是“RoledID”,第二个是“MenuIDs”。 'MenusIDs' 是一个逗号分隔的菜单 ID 列表,需要使用 RoledID 插入。 RoleId 只是一个 INT,我们需要将这个 RoledID 放在每个 MenuID 上。我的表“RolesMenus”包含两列,一列用于 MenuID,一列用于 RoleID。

现在我需要拆分 MenuID 并将每个 MenuID 插入 RoleID。

如何为它写一个存储过程?

【问题讨论】:

    标签: mysql stored-procedures


    【解决方案1】:

    您可以构建一个 INSERT 查询(因为语句允许插入多条记录)并使用 prepared statements 运行它,例如-

    SET @MenuIDs = '1,2,3';
    SET @RoledID = 100;
    
    SET @values = REPLACE(@MenuIDs, ',', CONCAT(', ', @RoledID, '),('));
    SET @values = CONCAT('(', @values, ', ', @RoledID, ')'); -- This produces a string like this -> (1, 100),(2, 100),(3, 100)
    
    SET @insert = CONCAT('INSERT INTO RolesMenus VALUES', @values); -- Build INSERT statement like this -> INSERT INTO RolesMenus VALUES(1, 100),(2, 100),(3, 100)
    
    -- Execute INSERT statement
    PREPARE stmt FROM @insert;
    EXECUTE stmt;
    DEALLOCATE PREPARE stmt;
    

    如你所见,它可以在没有存储过程的情况下完成。

    【讨论】:

      【解决方案2】:

      试一试。如果 MenuIDs 字符串不符合“menuId,menuId,menuId”,则可能需要进行一些调整。

      另外我不知道 menuId 列在您的目标表中是什么数据类型(INT?),因此您可能还需要进行一些数字检查(以防 '1,2,3,banana,4,5'作为 MenuIds 输入参数传入)。

      DELIMITER $$
      
      DROP PROCEDURE IF EXISTS `insert_role_menuids`$$
      
      CREATE PROCEDURE `insert_role_menuids`(IN RoleID INT,IN MenuIDs varchar(500))
      BEGIN
      declare idx,prev_idx int;
      declare v_id varchar(10);
      
      set idx := locate(',',MenuIDs,1);
      set prev_idx := 1;
      
      WHILE idx > 0 DO
       set v_id := substr(MenuIDs,prev_idx,idx-prev_idx);
       insert into RolesMenus (RoleId,MenuId) values (RoleID,v_id);
       set prev_idx := idx+1;
       set idx := locate(',',MenuIDs,prev_idx);
      END WHILE;
      
      set v_id := substr(MenuIDs,prev_idx);
      insert into RolesMenus (RoleId,MenuId) values (RoleID,v_id);
      
      END$$
      DELIMITER ;
      

      【讨论】:

      • 将由“,”分隔的字符串转换为表格的出色程序!
      【解决方案3】:

      对于这个解决方案,你必须创建一个名为 split_table 的表,如果你需要它可以有一个 id(autoincrement) 并且必须有一个用于存储值的列(我称之为 valor)

      DELIMITER $$
      
      USE `dbaname`$$
      
      DROP PROCEDURE IF EXISTS `Split`$$
      
      CREATE DEFINER=`root`@`localhost` PROCEDURE `Split`(
          IN cadena VARCHAR(8000),
          IN delimitador VARCHAR(10)
          )
      BEGIN
      
          TRUNCATE split_table;
      
          SET @posicion = 1;
          SET @ldel = LENGTH(delimitador);     
          SET @valor = SUBSTRING_INDEX(cadena, delimitador, 1);
      
          WHILE @valor <> '' AND @posicion > 0 DO
      
              SET @valor = SUBSTRING_INDEX(cadena, delimitador, 1);
      
              INSERT INTO split_table(valor) VALUES (@valor);
      
              SET @posicion = POSITION(delimitador IN cadena);
              SET @largo = LENGTH(cadena);
      
              IF @largo >= @posicion THEN
                  SET cadena = SUBSTR(cadena, @posicion + @ldel, @largo - @posicion);
                  SET @valor = SUBSTRING_INDEX(cadena, delimitador, 1);
              ELSE
                  SET @posicion = 0;
              END IF;
      
          END WHILE;
      
          END$$
      
      DELIMITER ;
      

      【讨论】:

        【解决方案4】:

        首先创建过程

        CREATE DEFINER=`root`@`localhost` PROCEDURE `split_str_save_to_tmp_table`(
        IN _str TEXT,
        IN _table_name VARCHAR(80)
        )
        BEGIN
        
        #DROP FIRST OLD TABLE
        SET @q = CONCAT('DROP TEMPORARY TABLE IF EXISTS ', _table_name);
        PREPARE st FROM @q;
        EXECUTE st;
        
        #CREATE TABLE
        SET @q = CONCAT('CREATE TEMPORARY TABLE ', _table_name, '(id INT UNSIGNED NOT NULL PRIMARY KEY (id) )' );
        PREPARE st FROM @q;
        EXECUTE st;
        
        SET @ids = REPLACE(_str, ',', '),(');
        SET @ids = CONCAT('(', @ids, ')');
        
        #INSERT INTO TABLE
        SET @q = CONCAT('INSERT INTO ' ,  _table_name ,' VALUES');
        SET @q = CONCAT(@q, @ids);  
        
        PREPARE st FROM @q;
        EXECUTE st;
        DEALLOCATE PREPARE st;
        
        END
        

        然后调用

        call split_str_save_to_tmp_table('1,2,3,4,5', 'tmp_split_product');
        
        SELECT * FROM tmp_split_product
        

        【讨论】:

          【解决方案5】:

          AFAIK MySQL 确实具有拆分字符串的功能。这是字符串相关函数的 MySQL 手册。在 cmets 部分应该有一些关于使用子字符串函数拆分字符串但不是真正可用的解决方法的信息: MySQL manual

          【讨论】: