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
_をシミュレートするその他の方法はありますか?
私の解決策は 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を取得するのを防ぐために、トランザクションを持つことは必須です。