sql >> データベース >  >> RDS >> Mysql

共通データのコンマ区切り値のグループ化

    それは可能ですが、これがあなたの非常に大きなテーブルにどれくらいの時間がかかるかはわかりません。すべてのグループを保持する新しいテーブルを作成することが許可されていると想定し、グループ列にデータが入力されるとそこに番号が表示されます。

    また、これはライブテーブルでは実行できません。書くことができないので、私のデザインの制限ではありません。値7と「6,7」の新しい行を追加するとどうなるか考えてみてください。これにより、グループ1と2がブリッジされ、すべての作業を削除する必要があります。

    このプロシージャは、テーブルに追加されるたびに再実行する必要があります。それが受け入れられない場合は、一度実行してから、値を維持し、必要に応じてグループをマージするトリガーに置き換えます。

    手順は次のとおりです。モジュール化の恩恵を受ける可能性がありますが、機能します。 Jay Pipes split_string を使用しました 機能し、それを含めました。

    まず、DDLといくつかのテストデータ

    CREATE TABLE `company` (
      `col1` int(11) DEFAULT NULL,
      `col2` varchar(100) DEFAULT NULL,
      `grp` int(11) DEFAULT NULL
    );
    
    CREATE TABLE `groups` (
      `number` int(11) NOT NULL DEFAULT '0',
      `grp` int(11) NOT NULL DEFAULT '0',
      `processed` tinyint(1) DEFAULT NULL,
      PRIMARY KEY (`number`,`grp`),
      KEY `grp` (`grp`)
    );
    
    insert into company (col1, col2) values 
    (1,'2,3,4'),       
    (2,'5,6'),        
    (3,'1,2,5'),
    (4,'7,8'),
    (5,'11,3'),
    (6,'22,8');
    

    そして今、手順

    use test;
    
    drop procedure if exists group_it;
    delimiter //
    
    create procedure group_it ()
    begin                        
      declare current_group int default 0;
      declare ids varchar(100);
    
      -- clear out all data from before
      update company set grp = null;
      truncate groups;
    
      main: loop                                
        -- take one unmapped (new group)
        set ids := null;
        select col2 into ids from company where grp is null limit 1;
        if ids is null then
          leave main;
        end if;
        set current_group := current_group + 1;
    
        --  put each value into groups table and mark as unprocessed
        call split_string(ids, ',');
        insert into groups select value, current_group, false from SplitValues;
    
        -- while unprocessed value in groups
        begin
          declare unprocessed int;
    
          unprocessed: loop
            set unprocessed = null;
            select number
              into unprocessed
              from groups
             where not processed
             limit 1;
    
            if unprocessed is null then
              leave unprocessed;
            end if;
    
            begin
              -- find all rows in company that matches this group
              declare row_id int;
              declare ids2 varchar(100);
    
              declare cur2_done boolean;
              declare cur2 cursor for
                select col1, col2 
                  from company
                 where col2 regexp concat('^', unprocessed, '$')
                    or col2 regexp concat('^', unprocessed, ',')
                    or col2 regexp concat(',', unprocessed, '$')
                    or col2 regexp concat(',', unprocessed, ',');
    
              declare continue handler for not found set cur2_done := true;
    
              open cur2;    
              numbers: loop
                set cur2_done := false;
                fetch cur2 into row_id, ids2; 
                if cur2_done then
                    close cur2;
                    leave numbers;
                end if;
    
                update company set grp = current_group where col1 = row_id;
                --  add all new values to groups marked as unprocessed
                call split_string(ids2, ',');   
                insert ignore into groups select value, current_group, false from SplitValues;
              end loop numbers;
              update groups set processed = true where number = unprocessed;
            end;
          end loop unprocessed;
        end;
      end loop main;
    end//
    
    delimiter ;         
    

    これはJayPipesのsplit_stringです

    DELIMITER //
    
    DROP PROCEDURE IF EXISTS split_string //
    CREATE PROCEDURE split_string (
    IN input TEXT
    , IN `delimiter` VARCHAR(10)
    )
    SQL SECURITY INVOKER
    COMMENT
    'Splits a supplied string using using the given delimiter,
    placing values in a temporary table'
    BEGIN
    DECLARE cur_position INT DEFAULT 1 ;
    DECLARE remainder TEXT;
    DECLARE cur_string VARCHAR(1000);
    DECLARE delimiter_length TINYINT UNSIGNED;
    
    DROP TEMPORARY TABLE IF EXISTS SplitValues;
    CREATE TEMPORARY TABLE SplitValues (
    value VARCHAR(1000) NOT NULL PRIMARY KEY
    ) ENGINE=MyISAM;
    
    SET remainder = input;
    SET delimiter_length = CHAR_LENGTH(delimiter);
    
    WHILE CHAR_LENGTH(remainder) > 0 AND cur_position > 0 DO
    SET cur_position = INSTR(remainder, `delimiter`);
    IF cur_position = 0 THEN
    SET cur_string = remainder;
    ELSE
    SET cur_string = LEFT(remainder, cur_position - 1);
    END IF;
    IF TRIM(cur_string) != '' THEN
    INSERT INTO SplitValues VALUES (cur_string);
    END IF;
    SET remainder = SUBSTRING(remainder, cur_position + delimiter_length);
    END WHILE;
    
    END //
    
    DELIMITER ;
    



    1. 1つのクエリで2つのテーブルを更新するにはどうすればよいですか?

    2. C#およびMySQL .NETコネクタ-汎用クラスでのSQLインジェクション攻撃を防ぐ方法はありますか?

    3. 集計を含む式で集計関数を実行できないのに、その周りに新しいselectステートメントを作成することで実行できるのはなぜですか?

    4. これはMySQLの並べ替えのバグですか?