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

MySQL-区切り文字で区切られたIDを含む列を正規化する方法

    この質問に答え始めたとき、SQL Serverで非常に似たようなことをしたことがあるので、すばやく簡単にできると思いましたが、翻訳の概念を証明することで、この完全なソリューションに急成長しました。

    あなたの質問から明らかではなかった1つの警告は、プライマリIDとエイリアスIDを宣言するための条件があるかどうかです。たとえば、このソリューションでは、1に4のエイリアスを設定し、4に1のエイリアスを設定できます。これは、簡略化した質問例で提供されているデータと一致しています。

    この例のデータを設定するために、次の構造を使用しました:

    CREATE TABLE notnormal_customers (
      id INT NOT NULL PRIMARY KEY,
      aliases VARCHAR(10)
    );
    
    INSERT INTO notnormal_customers (id,aliases)
    VALUES
    (1,'|4|58|76'),
    (2,''),
    (3,''),
    (4,'|1|58|76'),
    (58,'|1|4|76'),
    (76,'|1|4|58');
    

    まず、1人の顧客と多くのエイリアスの1対多の関係を表すために、次のテーブルを作成しました。

    CREATE TABLE customer_aliases (
        primary_id INT NOT NULL,
        alias_id INT NOT NULL,
        FOREIGN KEY (primary_id) REFERENCES notnormal_customers(id),
        FOREIGN KEY (alias_id)   REFERENCES notnormal_customers(id),
        /* clustered primary key prevents duplicates */
        PRIMARY KEY (primary_id,alias_id)
    )
    

    最も重要なのは、カスタムSPLIT_STR 機能

    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, '');
    

    次に、すべての作業を行うためのストアドプロシージャを作成します。コードには、ソース参照へのコメントが注釈として付けられています。

    DELIMITER $$
    CREATE PROCEDURE normalize_customers()
    BEGIN
    
      DECLARE cust_id INT DEFAULT 0;
      DECLARE al_id INT UNSIGNED DEFAULT 0;
      DECLARE alias_str VARCHAR(10) DEFAULT '';
      /* set the value of the string delimiter */
      DECLARE string_delim CHAR(1) DEFAULT '|';
      DECLARE count_aliases INT DEFAULT 0;
      DECLARE i INT DEFAULT 1;
    
      /*
        use cursor to iterate through all customer records
        http://burnignorance.com/mysql-tips/how-to-loop-through-a-result-set-in-mysql-strored-procedure/
      */
      DECLARE done INT DEFAULT 0;
      DECLARE cur CURSOR FOR
          SELECT `id`, `aliases`
          FROM `notnormal_customers`;
      DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
    
      OPEN cur;
      read_loop: LOOP
    
        /*
          Fetch one record from CURSOR and set to customer id and alias string.
          If not found then `done` will be set to 1 by continue handler.
        */
        FETCH cur INTO cust_id, alias_str;
        IF done THEN
            /* If done set to 1 then exit the loop, else continue. */
            LEAVE read_loop;
        END IF;
    
        /* skip to next record if no aliases */
        IF alias_str = '' THEN
          ITERATE read_loop;
        END IF;
    
        /*
          get number of aliases
          https://pisceansheart.wordpress.com/2008/04/15/count-occurrence-of-character-in-a-string-using-mysql/
        */
        SET count_aliases = LENGTH(alias_str) - LENGTH(REPLACE(alias_str, string_delim, ''));
    
        /* strip off the first pipe to make it compatible with our SPLIT_STR function */
        SET alias_str = SUBSTR(alias_str, 2);
    
        /*
          iterate and get each alias from custom split string function
          https://stackoverflow.com/questions/18304857/split-delimited-string-value-into-rows
        */
        WHILE i <= count_aliases DO
    
          /* get the next alias id */
          SET al_id = CAST(SPLIT_STR(alias_str, string_delim, i) AS UNSIGNED);
          /* REPLACE existing values instead of insert to prevent errors on primary key */
          REPLACE INTO customer_aliases (primary_id,alias_id) VALUES (cust_id,al_id);
          SET i = i+1;
    
        END WHILE;
        SET i = 1;
    
      END LOOP;
      CLOSE cur;
    
    END$$
    DELIMITER ;
    

    最後に、次のように呼び出すだけで実行できます:

    CALL normalize_customers();
    

    次に、コンソールでデータを確認できます:

    mysql> select * from customer_aliases;
    +------------+----------+
    | primary_id | alias_id |
    +------------+----------+
    |          4 |        1 |
    |         58 |        1 |
    |         76 |        1 |
    |          1 |        4 |
    |         58 |        4 |
    |         76 |        4 |
    |          1 |       58 |
    |          4 |       58 |
    |         76 |       58 |
    |          1 |       76 |
    |          4 |       76 |
    |         58 |       76 |
    +------------+----------+
    12 rows in set (0.00 sec)
    


    1. SQL Server:テーブルの最大行数

    2. データベースの削除は、どのトランザクションでも実行する必要はありませんか?

    3. キャッチ可能な致命的なエラー:クラスmysqli_stmtのオブジェクトを文字列に変換できませんでした

    4. 「SQLServerを使用したMicrosoftAccessの最適化」プレゼンテーションをご覧ください