web-dev-qa-db-ja.com

InnoDBの複合キーでのAUTO_INCREMENTのトリック

MySQL MyISAM-Engineとは対照的に、複合主キーの1つのカラムが_AUTO_INCREMENT_として宣言されている場合、InnoDB-Engineは処理できません。たとえば、次の表について考えてみます。

_CREATE TABLE `ExampleTable` (
     `KeyPartA` mediumint(8) unsigned NOT NULL,
     `KeyPartB` smallint(5) unsigned NOT NULL,
     `Data` text NOT NULL,
     PRIMARY KEY (`KeyPartA`,`KeyPartB`)
) ENGINE=InnoDB;
_

ここで_AUTO_INCREMENT_をシミュレートするために、次のトリガーを作成することが可能であることを学びました

_DELIMITER $$

CREATE TRIGGER ExampleAutoIncrement BEFORE INSERT ON ExampleTable
FOR EACH ROW BEGIN
        SET NEW.KeyPartB = (
             SELECT IFNULL(MAX(KeyPartB), 0) + 1
             FROM ExampleTable
             WHERE KeyPartA  = NEW.KeyPartA
        );
END $$

DELIMITER ;
_

挿入が正しく機能し、自動インクリメントされた値が生成されるようになりました。残念ながら、新しく作成されたキー値を取得するための賢明な方法は考えられません。

実際の_AUTO_INCREMENT_列がないため、LAST_INSERT_ID()もCの対応する関数も返しません。また、MySQLはトリガーの終了後に値をリセットするため、ExampleAutoIncrementトリガー内でLAST_INSERT_ID(IFNULL(MAX(KeyPartB), 0) + 1)を使用して値を設定することはできません。

私が考えた唯一の方法は、挿入の後にSELECT MAX(KeyPartB) FROM ExampleTable WHERE KeyPartA = ?を発行することでした。これはトリガーの目的全体を無効にします。つまり手動で挿入および増分する前に、これを行うこともできます。

トリガーからインクリメントされたキー値を取得する方法、または_AUTO_INCREMENT_をシミュレートするその他の方法はありますか?

4
bodo

私の解決策は myisamからinnodb に移行することです。それは次のようになります:

  BEGIN;
  SELECT @id := IFNULL(MAX(id),0) + 1 FROM foo WHERE other = 123 FOR UPDATE;
  INSERT INTO foo
     (other, id, ...)
     VALUES
     (123, @id, ...);
  COMMIT;

別のスレッドが同じIDを取得するのを防ぐために、トランザクションを持つことは必須です。

4
Rick James