本記事では、OceanBaseデータベースにおけるLAST_INSERT_ID()関数の動作と、MySQLとの違いについて説明します。
LAST_INSERT_ID()関数は、直近のINSERT操作で生成された自動インクリメントID値を取得するために使用されます。OceanBaseデータベースでは、この関数の動作がMySQLといくつか異なり、主に以下の2点に表れています:
- セッションレベルのLAST_INSERT_ID:
LAST_INSERT_ID()関数によって読み取りおよび変更されます。 - プロトコルレベルのLAST_INSERT_ID:MySQLプロトコルのOKパケットで返される値
MySQLの動作
セッションレベルのLAST_INSERT_ID
セッション上のLAST_INSERT_IDはLAST_INSERT_ID(args)関数を使用して読み取りおよび変更できます。
-- セッション上のLAST_INSERT_IDを読み取る
SELECT LAST_INSERT_ID();
+------------------+
| LAST_INSERT_ID() |
+------------------+
| 0 |
+------------------+
-- 関数を使用してセッション上のLAST_INSERT_IDを変更する
SELECT LAST_INSERT_ID(10);
+--------------------+
| LAST_INSERT_ID(10) |
+--------------------+
| 10 |
+--------------------+
プロトコルレベルのLAST_INSERT_ID
MySQLプロトコルのOKパケット内のLAST_INSERT_IDです。DMLステートメントがセッション上のLAST_INSERT_IDを変更する場合、DML実行後にクライアントに返されるOKパケット内のLAST_INSERT_IDは、セッション上のLAST_INSERT_IDと等しくなります。そうでない場合、クライアントに返されるLAST_INSERT_IDは、DMLステートメントによって書き込まれた最後の行のデータに対応するAUTO_INCREMENT列の値となります。
OceanBaseデータベースとMySQLの互換性
完全互換の動作
OceanBaseデータベースは、以下の点でMySQLと完全に互換しています。
- セッションレベルでの
LAST_INSERT_IDの読み取りと変更:LAST_INSERT_ID()関数を使用して、セッション上の値を読み取りおよび変更します。 - 関数式による
LAST_INSERT_ID値の設定:LAST_INSERT_ID(expr)関数を使用して値を設定します。 - プロトコルレベルのOKパケット形式:クライアントへの応答パケット形式がMySQLと一致しています。
- REPLACEステートメント:最初のINSERT行または上書きされた自動インクリメント列の値を返します。
- INSERT ... ON DUPLICATE KEY UPDATE:すべての場合において、競合がない場合は最初のINSERT行の自動インクリメント列の値を返し、部分的な競合がある場合も最初のINSERT行の自動インクリメント列の値を返します。すべて競合する場合は、元の値を維持します。
- 複数行の挿入:最初のINSERT行の自動インクリメント列の値を返します。
- IGNOREステートメント:主キー競合によりデータが書き込まれなかった場合、
LAST_INSERT_IDは変更されません。
説明
セッション上では、OceanBaseもMySQLと同様に LAST_INSERT_ID を保持し、式を使用してセッション上の LAST_INSERT_ID を取得および変更します。OceanBaseの動作はMySQLと完全に一致します。
動作の違い
OceanBaseデータベースとMySQLの主な違いは以下の点です:
注意
OceanBaseは現在、クライアントへの応答パケット形式をMySQLと統一しています。つまり、OKパケットにはLAST_INSERT_ID情報が含まれていますが、この情報の表示は現在MySQLと完全には一致していません。DMLステートメントでAUTO_INCREMENT列に書き込む際、LAST_INSERT_IDの値の変化はMySQLと完全には互換性がありません。
INSERTステートメントで自動インクリメント列を手動指定する場合
MySQLの動作:
- 自動インクリメント列を手動指定:
LAST_INSERT_IDは変更されません。
OceanBaseデータベースの動作:
- 自動インクリメント列を手動指定:最初のINSERTの自動インクリメント列の値を返します。
説明
これはOceanBaseデータベースとMySQLのLAST_INSERT_IDの動作における主な違いです。OceanBaseデータベースの実装では、現在の自動インクリメント列の値が手動で指定されたものか、自動インクリメントサービスから取得した連番かを区別できないため、LAST_INSERT_IDの表示は一貫しています。
-- テストテーブルを作成
CREATE TABLE `t1` (
`c1` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
`c2` INT DEFAULT NULL,
PRIMARY KEY (`c1`),
UNIQUE KEY `c2` (`c2`)
);
-- 例1:LAST_INSERT_ID関数を使用して値を変更する(MySQLとOceanBaseの動作は一致する)
UPDATE t2 SET c1 = LAST_INSERT_ID(100) + c1 WHERE c2 = 1;
SELECT LAST_INSERT_ID();
+------------------+
| LAST_INSERT_ID() |
+------------------+
| 100 |
+------------------+
-- 例2:INSERTステートメント(MySQLとOceanBaseの動作は一致する)
INSERT INTO t1(c2) VALUES (1);
SELECT LAST_INSERT_ID();
+------------------+
| LAST_INSERT_ID() |
+------------------+
| 1 |
+------------------+
-- 例3:REPLACEステートメント(MySQLとOceanBaseの動作は一致する)
REPLACE INTO t1(c2) VALUES (2);
SELECT LAST_INSERT_ID();
+------------------+
| LAST_INSERT_ID() |
+------------------+
| 2 |
+------------------+
-- 例4:複数行挿入(MySQLとOceanBaseはどちらも最初の行のIDを返す)
INSERT INTO t1(c2) VALUES (3), (4);
SELECT LAST_INSERT_ID();
+------------------+
| LAST_INSERT_ID() |
+------------------+
| 3 |
+------------------+
詳細比較表
DMLステートメントのタイプ |
シナリオ |
MySQLセッション上のLAST_INSERT_ID |
OceanBaseセッション上のLAST_INSERT_ID |
相違点の説明 |
|---|---|---|---|---|
| INSERT | 主キー競合 | 変更なし | 変更なし | 一致 |
| 主キー競合なし(自動インクリメント列を指定しない場合) | 最初のINSERTの自動インクリメント列の値 | 最初のINSERTの自動インクリメント列の値 | 一致 | |
| 主キー競合なし(手動で自動インクリメント列を指定した場合) | 変更なし | 最初のINSERTの自動インクリメント列の値 | OceanBaseは値を更新します | |
| REPLACE | 最初のINSERTまたは上書きされた行の自動インクリメント列の値 | 最初のINSERTまたは上書きされた行の自動インクリメント列の値 | 一致 | |
| INSERT ... ON DUPLICATE KEY | 全て競合なし | 最初のINSERTの自動インクリメント列の値 | 最初のINSERTの自動インクリメント列の値 | 一致 |
| 一部競合 | 最初のINSERTの自動インクリメント列の値 | 最初のINSERTの自動インクリメント列の値 | 一致 | |
| 全部競合 | 変更なし | 変更なし | 一致 |
互換性計画
説明
上記の動作が有効なバージョン:V4.3.3およびV4.4.1以降のバージョン。OceanBaseデータベースV4.3.5バージョンでは、V4.3.5 BP2バージョンからサポートされます。