2012-03-30 7 views
3

私はそれらにグループを割り当てるためCOL1のID INT、VARCHAR(カンマ区切り値)としてCOL2および列3とのテーブルを持っています。 表は共通データにカンマ区切り値をグループ化

col1   col2  group 
.............................. 
     1  2,3,4  
     2  5,6   
     3  1,2,5 
     4  7,8 
     5  11,3 
     6  22,8 

のように見えるこれは、今私は、出力がグループを割り当てるための

col1   col2  group 
.............................. 
     1  2,3,4  1 
     2  5,6   1 
     3  1,2,5  1 
     4  7,8   2 
     5  11,3  1 
     6  22,8  2 

ロジックのように見えるように彼らに何のグループを割り当てる必要がない、実際のデータのサンプルのみですいいえ、col2の文字列のすべてのコンマ区切り値は、同じグループ番号でなければならないということです。col2では '2'がそこにありますが、同じグループ番号でなければなりません。しかし、2,3,4は一緒ですcol2のどこに同じグループが割り当てられるかにかかわらず、3つのint値すべて。 大部分が2,3,4および1,2,5です。col2の両方に2があります。したがって、int 1,2,3,4,5はすべて同じグループ番号を割り当てる必要があります。 col2との一致でストアドプロシージャを試しましたが、目的の結果が得られません

何百万ものレコードを持つ元のテーブルから新しいテーブルを作ることができないので、ほとんどのimp(私は正規化を使用できません)私の文脈では役に立たない。これまでに達成


...... IHAVEは、グループ列自動インクリメントを設定してから、この手順を書いた: -

BEGIN 
    declare cil1_new,col2_new,group_new int; 
    declare done tinyint default 0; 
    declare group_new varchar(100); 
    declare cur1 cursor for select col1,col2,`group` from company ; 
    DECLARE CONTINUE HANDLER FOR NOT FOUND SET done=1; 
    open cur1; 
    REPEAT 
    fetch cur1 into col1_new,col2_new,group_new; 
    update company set group=group_new where 
    match(col2) against(concat("'",col2_new,"'")); 
    until done end repeat; 
    close cur1; 
    select * from company; 
END 

この手順を、何のシンテックスミスを作業していないが、probleがあるさ私はexectly望ましい結果を達成していません。

+4

をsplit_stringジェイパイプであります別のテーブルに列? – cha0site

+7

リレーショナルデータベースにCSVを格納することは、バイクを使用して車を牽引することと同じです。 – Corbin

+1

いいえ、本当のテーブルがgbのもので大きすぎるので、私はそれを正規化する余裕がありません。 –

答えて

0

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

また、ライブテーブルでは実行できません。それは私のデザインの制限ではないので、それを書くことはできません。値7と '6,7'を持つ新しい行を追加すると、グループ1と2を橋渡しし、すべての作業を削除する必要があります。

このprocがテーブルへの追加があるたびに再実行する必要があります。それが許容できない場合は、一度実行してから、値を保持し、必要なときにグループをマージするトリガーに置き換えます。

ここに手順があります。それはいくつかのモジュール化の恩恵を受けることができますが、それは動作します。私はジェイパイプス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 ;   

これは、あなたのCSVを正規化関わるソリューションを受け入れるだろう

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 ;