2017-10-10 3 views
1

のMySQL 5.5(エンジンMyISAM)の2つのテーブルのスキーマを比較するときに、欠落しているカラムを追加することを目標としています。引数p_table1は、p_table2が比較され、 "同期化"されるモデルテーブル名です。MySQL 2つのテーブルのスキーマを同期する手順

呼び出されると、何も起こりません。エラーも何も起こりません。私はいくつかの変数を記録しようとしましたが、どちらもうまくいきませんでした。

コードに間違いがありますか?

CREATE PROCEDURE synchronize_tables(p_table1 VARCHAR(64), p_table2 VARCHAR(64), p_schema_name VARCHAR(64)) 
BEGIN 
    DECLARE v_done INT default false; 
    DECLARE v_actual_column_name VARCHAR(64); 
    DECLARE v_does_columns_exist INT default true; 
    DECLARE v_column_type LONGTEXT; 
    DECLARE v_column_default LONGTEXT; 
    DECLARE v_is_nullable VARCHAR(3); 

    DECLARE v_cur CURSOR FOR 
     SELECT column_name 
     FROM INFORMATION_SCHEMA.COLUMNS 
     WHERE table_name = p_table1 
     AND table_schema = p_schema_name; 

    DECLARE CONTINUE HANDLER FOR NOT FOUND SET v_done = TRUE; 
    DECLARE EXIT HANDLER FOR SQLEXCEPTION 
    BEGIN 
     OPEN v_cur; 

     read_loop: LOOP 
      FETCH v_cur INTO v_actual_column_name; 
      IF v_done THEN 
       LEAVE read_loop; 
      END IF; 

      SELECT count(*) INTO v_does_columns_exist 
      FROM INFORMATION_SCHEMA.COLUMNS 
      WHERE table_name = p_table2 
      AND table_schema = p_schema_name 
      AND column_name = v_actual_column_name; 

      IF NOT v_does_columns_exist THEN 
       SELECT column_type, COLUMN_DEFAULT, IS_NULLABLE 
       INTO v_column_type, v_column_default, v_is_nullable 
       FROM INFORMATION_SCHEMA.COLUMNS 
       WHERE table_name = p_table1 
       AND table_schema = p_schema_name 
       AND column_name = v_actual_column_name; 

       SET @stmt_text = CONCAT('ALTER TABLE ', p_schema_name, '.', p_table2, 
        ' ADD COLUMN ', v_actual_column_name, ' ', v_column_type, ' ', IF(upper(v_is_nullable) = 'NO', 'NOT NULL', ''), 
        ' DEFAULT ', v_column_default); 

       prepare v_stmt FROM @stmt_text; 
       execute v_stmt; 
       deallocate prepare v_stmt; 
      END IF; 
     END LOOP; 

     CLOSE v_cur; 
    END; 
END 
+0

あなた自身ではなく、[mysqldbcompare](https://dev.mysql.com/doc/mysql-utilities/1.5/en/mysqldbcompare.html)を使用したことはありますか? –

+0

@BillKarwin私は比較するdbモデルを持っていないので、私はmysqldbcompareを書き直そうとしていません。実際、私は、レイガシコードベースとそれほど良いプロジェクトの選択肢に対して何らかの対処法を実行しようとしています。問題は、そのストアドプロシージャが期待どおりに動作しない理由です。つまり、ここで問題となるのは、プロシージャを正しく動作させることができず、ドキュメンテーションを見ても何が起きているのか分からないということです。 – susanoobit

答えて

1

私はいくつかの問題を発見しました。

まず、カーソルコードのほとんどがEXIT HANDLER FOR SQLEXCEPTIONの内側にあることです。このブロックは、エラーが発生した場合にのみ実行されます。したがって、通常このブロックは決して実行されません。

第2に、ALTER TABLEステートメントを構成する列をCONCAT()しますが、1つ以上の列をNULLにすることができます。 NULLで文字列をCONCAT()すると、concat操作全体の結果はNULLになります。だからあなたは、NULLがNULLでないものにデフォルト設定されていることを確認する必要があります。

私のテストでは、列のデフォルトはNULLであることがよくあります。これをALTER TABLEステートメントのキーワード "NULL"にしたいと考えています。また、デフォルト値がNULLでない場合は、通常のデフォルト値が文字列または日付であり、引用符で囲まれていない可能性があるため、引用符で囲むことをお勧めします。解決策:QUOTE()は、文字列を適切に引用する組み込み関数であり、NULLをキーワード "NULL"に変換します。

は、ここで私が動作するようになったものです:識別子が区切られていない

  • CREATE PROCEDURE synchronize_tables(p_table1 VARCHAR(64), 
        p_table2 VARCHAR(64), p_schema_name VARCHAR(64)) 
    BEGIN 
        DECLARE v_done INT default false; 
        DECLARE v_actual_column_name VARCHAR(64); 
        DECLARE v_does_columns_exist INT default true; 
        DECLARE v_column_type LONGTEXT; 
        DECLARE v_column_default LONGTEXT; 
        DECLARE v_is_nullable VARCHAR(3); 
    
        DECLARE v_cur CURSOR FOR 
         SELECT column_name 
         FROM INFORMATION_SCHEMA.COLUMNS 
         WHERE table_name = p_table1 
         AND table_schema = p_schema_name; 
    
        DECLARE CONTINUE HANDLER FOR NOT FOUND SET v_done = TRUE; 
    
        OPEN v_cur; 
    
        read_loop: LOOP 
         FETCH v_cur INTO v_actual_column_name; 
         IF v_done THEN 
          LEAVE read_loop; 
         END IF; 
    
         SELECT count(*) INTO v_does_columns_exist 
         FROM INFORMATION_SCHEMA.COLUMNS 
         WHERE table_name = p_table2 
         AND table_schema = p_schema_name 
         AND column_name = v_actual_column_name; 
    
         IF NOT v_does_columns_exist THEN 
          SELECT column_type, COLUMN_DEFAULT, IS_NULLABLE 
          INTO v_column_type, v_column_default, v_is_nullable 
          FROM INFORMATION_SCHEMA.COLUMNS 
          WHERE table_name = p_table1 
          AND table_schema = p_schema_name 
          AND column_name = v_actual_column_name; 
    
          SET @stmt_text = CONCAT('ALTER TABLE ', 
           p_schema_name, '.', p_table2, 
           ' ADD COLUMN ', v_actual_column_name, ' ', 
           v_column_type, ' ', 
           IF(upper(v_is_nullable) = 'NO', 'NOT NULL', ''), 
           ' DEFAULT ', QUOTE(v_column_default)); 
          PREPARE v_stmt FROM @stmt_text; 
          EXECUTE v_stmt; 
          DEALLOCATE prepare v_stmt; 
         END IF; 
        END LOOP; 
    
        CLOSE v_cur; 
    END 
    

    は、このアプローチには他の問題があります。

  • 1つのALTERに複数の列を追加することはできますが、欠落している列ごとに1つのALTER TABLEを生成します。
  • カラムがNULLではないのにデフォルトを持たない場合、正しいことはありません。

しかし、私はとにかくカーソルでこれをしません。

CREATE PROCEDURE synchronize_tables(p_table1 VARCHAR(64), 
    p_table2 VARCHAR(64), p_schema_name VARCHAR(64)) 
BEGIN 
    SELECT CONCAT(
     'ALTER TABLE `', C1.TABLE_SCHEMA, '`.`', p_table2, '` ', 
     GROUP_CONCAT(CONCAT(
     'ADD COLUMN `', C1.COLUMN_NAME, '` ', C1.COLUMN_TYPE, 
     IF(C1.IS_NULLABLE='NO', ' NOT NULL ', ''), 
     IF(C1.COLUMN_DEFAULT IS NULL, '', 
      CONCAT(' DEFAULT ', QUOTE(C1.COLUMN_DEFAULT))) 
     ) SEPARATOR ', ' 
    ) 
    ) INTO @stmt_text 
    FROM INFORMATION_SCHEMA.COLUMNS AS C1 
    LEFT OUTER JOIN INFORMATION_SCHEMA.COLUMNS AS C2 
     ON C1.TABLE_SCHEMA=C2.TABLE_SCHEMA 
     AND C2.TABLE_NAME=p_table2 
     AND C1.COLUMN_NAME=C2.COLUMN_NAME 
    WHERE C1.TABLE_SCHEMA=p_schema_name AND C1.TABLE_NAME=p_table1 
     AND C2.TABLE_SCHEMA IS NULL; 

    PREPARE v_stmt FROM @stmt_text; 
    EXECUTE v_stmt; 
    DEALLOCATE prepare v_stmt; 
END 

これは、すべての列を追加する ALTER TABLEを作る:簡単な方法があります。これは識別子を区切ります。それはより良いNOT NULLを処理します。

しかし、たとえ私の解決策はまだ問題があります:あなたは人口table2のするデフォルトとNOT NULL列を追加しようとした場合

  • は、エラーが発生します。
  • 余分な列は2番目のテーブルに存在しますが、最初のテーブルには存在しません。
  • 制約、インデックス、トリガー、プロシージャ、またはビューを同期しません。

データベース構造を完全に同期させるのは非常に複雑な作業です。私は、このためにMySQLのストアドプロシージャ言語を使用しようとすると、困難な作業をさらに困難にすることを提案します。

MySQLのストアドプロシージャの実装では問題があります。

  • 不十分なドキュメント。
  • パッケージをサポートしていません。
  • 標準手順または豊富な関数ライブラリはありません。
  • 実際のデバッガはありません(いくつかのツールが試していますが、偽装しています)。
  • コンパイルされたプロシージャを永続化するためのサポートはありません。プロシージャは、それらを使用するすべてのセッションによって再コンパイルされます。

多くの場合、OracleまたはMicrosoft SQL Serverの手順を使用することに慣れている開発者は、をMySQLストアドプロシージャから離れることをお勧めします。

関連する問題