今回は MySQL の AUTO_INCREMENT の属性が付けられたカラムの値の取得について説明します。
AUTO_INCREMENT 属性を付けられるカラムはデータ型が整数型です。
INSERT SQLにより AUTO_INCREMENT 属性カラムを指定しない場合、順次+1された値が割り当てられます。
以下の様に、テスト用のテーブルを作成します。
「id」と「name」の2つのみのカラムを持ち、「id」に AUTO_INCREMENT を設定します。
MariaDB [pdo]> CREATE TABLE `test_auto` ( -> `id` int(11) NOT NULL AUTO_INCREMENT, -> `name` varchar(100) DEFAULT NULL, -> PRIMARY KEY (id) -> ) ENGINE=InnoDB DEFAULT CHARSET=utf8; Query OK, 0 rows affected (0.91 sec) MariaDB [pdo]> show columns from `test_auto`; +-------+--------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +-------+--------------+------+-----+---------+----------------+ | id | int(11) | NO | PRI | NULL | auto_increment | | name | varchar(100) | YES | | NULL | | +-------+--------------+------+-----+---------+----------------+ 2 rows in set (0.05 sec) MariaDB [pdo]>
このテーブルにデータを追加してみます。
MariaDB [pdo]> INSERT INTO `test_auto` (`name`) VALUES ('test001'); Query OK, 1 row affected (0.05 sec) MariaDB [pdo]> select * from `test_auto`; +----+---------+ | id | name | +----+---------+ | 1 | test001 | +----+---------+ 1 row in set (0.00 sec)
MySQL には最後の AUTO_INCREMENT カラムの値を取得できる LAST_INSERT_ID() 関数があります。
MariaDB [pdo]> SELECT LAST_INSERT_ID() FROM `test_auto`; +------------------+ | LAST_INSERT_ID() | +------------------+ | 1 | +------------------+ 1 row in set (0.00 sec)
MySQL の LAST_INSERT_ID() 関数と同じ機能が PDO クラスの lastInsertId メソッドで行えます。
上記の「test_auto」テーブルにデータを登録し、 lastInsertId メソッドを呼出してみます。
<?php /* [pdo17.php] */ ob_start(function($buf){ return mb_convert_encoding($buf, 'SJIS', 'UTF-8'); }); // MySQLデータベースに接続 $dsn = "mysql:host=localhost;dbname=pdo;"; $user = 'root'; $password = 'password'; try { // PDOクラス生成(データベース接続) $pdo = new PDO($dsn, $user, $password); } catch (PDOException $e) { // エラー発生 die('データベース接続ERROR:'.$e->getMessage()."\n"); } // レコード登録(INSERT)SQL:[id]は AUTO_INCREMENT なので指定しない $sql = "insert into `test_auto` (name) VALUE (:name)"; // SQL文の準備 $pdostmt = $pdo->prepare($sql); // SQL文の実行 $ret = $pdostmt->execute(array(":name" => 'test002')); // [test_auto.id]の取得 $id = $pdo->lastInsertId(); echo "lastInsertId() = $id \n"; // 接続を閉じる $pdo = null; ?>
これを実行すると以下の様に表示されます。2件目のレコードが追加されたので「id」に「2」が返されるのがわかります。
C:\xampp\htdocs\_test>php pdo17.php lastInsertId() = 2
テーブルの中身を見てみます。
MariaDB [pdo]> select * from `test_auto`; +----+---------+ | id | name | +----+---------+ | 1 | test001 | | 2 | test002 | +----+---------+ 2 rows in set (0.05 sec) MariaDB [pdo]>
この lastInsertId メソッドは、登録した直後のデータの「id」を取得しますが、これの必要な場面とは何があるのでしょうか。
例えば、売上データで、売上ヘッダテーブルと売上明細テーブルを分けて持つ場合です。
売上ヘッダデータと売上明細データは 1:N の関係で、同じ「id」で紐づけられると思います。
最初に売上ヘッダデータを登録し、その直後に lastInsertId メソッドで「id」を取得し、 売上明細にはその「id」と明細の連番で売上明細テーブルに登録するといった手順になると思います。
■トランザクション処理の中の AUTO_INCREMENT について
トランザクション処理の中で1件データを追加し、その後で明示的にエラーが発生するSQL文を実行し、ロールバックを行わせてみます。 以下にそのスクリプトを示します。
<?php /* [pdo18.php] */ ob_start(function($buf){ return mb_convert_encoding($buf, 'SJIS', 'UTF-8'); }); // MySQLデータベースに接続 $dsn = "mysql:host=localhost;dbname=pdo;"; $user = 'root'; $password = 'password'; try { // PDOクラス生成(データベース接続) $pdo = new PDO($dsn, $user, $password); } catch (PDOException $e) { // エラー発生 die('データベース接続ERROR:'.$e->getMessage()."\n"); } // トランザクション開始 $ret = $pdo->beginTransaction(); if ($ret == true) { // トランザクション開始OK try { // レコード登録(INSERT)SQL:[id]は AUTO_INCREMENT なので指定しない $sql = "insert into `test_auto` (name) VALUE (:name)"; $pdostmt = $pdo->prepare($sql); $ret = $pdostmt->execute(array(":name" => 'test003')); // [test_auto.id]の取得 $id = $pdo->lastInsertId(); echo "lastInsertId() = $id \n"; // エラーの発生するレコード登録(INSERT)SQL $sql = "insert into `test_auto` (name) VALUE ()"; $pdostmt = $pdo->prepare($sql); $ret = $pdostmt->execute(); // この時点でエラー発生 } catch (Exception $e) { // 何かスクリプト上のエラーが在った!! $ret = false; } // トランザクション終了処理 if ($ret == true) { // コミット $pdo->commit(); echo "commit() \n"; } else { // ロールバック $pdo->rollback(); echo "rollback() \n"; } // [test_auto.id]の取得 $id = $pdo->lastInsertId(); echo "lastInsertId() = $id \n"; } else { // トランザクション開始NG } // 接続を閉じる $pdo = null; ?>
これを実行すると以下の様に表示されます。 登録実行後、最初の lastInsertId メソッドでは「3」が返されますが、 次の INSERT 処理ではエラーが発生し、ロールバック処理が行われます。 この後で、 lastInsertId メソッドを実行してもトランザクション開始前の状態に戻るため、「0」が返ります。
C:\xampp\htdocs\_test>php pdo18.php lastInsertId() = 3 rollback() lastInsertId() = 0
この状態で、テーブルの属性を SHOW TABLE コマンドで見てみます。
MariaDB [pdo]> SHOW TABLE STATUS LIKE 'test_auto'\G *************************** 1. row *************************** Name: test_auto Engine: InnoDB Version: 10 Row_format: Compact Rows: 3 Avg_row_length: 5461 Data_length: 16384 Max_data_length: 0 Index_length: 0 Data_free: 0 Auto_increment: 4 Create_time: 2020-12-04 18:39:53 Update_time: NULL Check_time: NULL Collation: utf8_general_ci Checksum: NULL Create_options: Comment: 1 row in set (0.00 sec)
test_auto の Auto_increment は「4」となり、次の追加処理が行われた時にはその値「4」が設定されます。
トランザクション中に Auto_increment の変化が在ったのですがロールバックしても値は「4」のままとなります。
上記のソースでは「3」の値でINSERTしたのですが、その後のエラーSQLでロールバックされ「3」は欠番となります。
この結果は少し、不都合な感じもしますが、複数のマシンから同じスクリプトが起動され、 それぞれが別々の Auto_increment の値を取る様にするためには必要なことだと思います。
コメント