読者です 読者をやめる 読者になる 読者になる

【WordPressカスタマイズ相談】現役プログラマが解決策を教えます

WordPress(ワードプレス)のお悩み、うまくいかなくてお困りなこと、不具合調査、新規制作依頼まで、ウェブアプリケーションエンジニアがあなたをサポートします。

PHPでユニーク制約カラムへの登録はMySQLのエラーコード23000をみるかIGNOREを使うとスムーズ

MySQL

ユニーク制約とは、重複した値を登録することができないことを意味します。


たとえば1000件のデータを作る場合、1件ずつ重複チェックをしてから登録という流れになると思います。
テーブルロックしないと他から登録されたり、1000件の作成データ内にも重複がないかチェックしたりと結構面倒だったりします。


そこでスムーズな登録処理方法の1つとして、ユニーク制約のあるテーブルへの連続登録PHPプログラムをご紹介します。
対象のカラムがユニーク制約である前提です。

MySQLのエラーコード23000

ユニーク制約のかかったカラムに、登録されている値と同じ値を登録してみます。
すると、以下のようなエラーが返ってきます。

SQLSTATE[23000]: Integrity constraint violation Duplicate entry

「重複した値を登録しようとしたよ」というエラーです。
PHPではこの23000エラーを判定して登録処理を行います。

エラーコードを判断して連続登録

ユニーク規約カラム「コード」に乱数文字列を生成して1000件登録するサンプルコードです。

<?php
// データベースに接続
$pdo = new PDO(
	'mysql:dbname=table;host=127.0.0.1;charset=utf8',
	'user',
	'pass',
	[
		PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
		PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
	]
);
//保存できた件数
$saved_count =0;
//1000件登録したいとする
while ($insert_count < 1000) {
	//3文字のランダム英数字を生成する
	$code = substr(str_shuffle('1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'), 0, 3);

	try {
		//トランザクション開始
		$pdo->beginTransaction();
		//INSERTクエリ
		$insert_sql = "INSERT INTO t_code (code, created) VALUES (:code, CURRENT_TIMESTAMP)";
		$stmt = $pdo->prepare($insert_sql);
		$stmt->bindParam(':code', $code, PDO::PARAM_STR);

		//実行
		$flg = $stmt->execute();
		//コミット
		$pdo->commit();

		//登録成功件数をカウント
		$insert_count++;
	} catch (PDOException $pdo_ex) {
		//ロールバック
		$this->rollback();
		//エラーコード重複登録はスキップ
		if ($pdo_ex->getCode() == 23000) continue;
		//重複登録以外エラー
		//終了orスキップなどの処理を入れる
	}
}


ポイントは
・whileを使い1000件登録するまでループする
トランザクションを用いる
・PDOExceptionを使い、重複登録時には例外をキャッチする
・キャッチしたエラーコードを見て(23000だったら)スキップする

の4つ。

これで1000件のユニークなデータを登録することができます。

エラーを発生させずに終了するIGNORE

MySQLにはIGNOREという便利な機能も存在します。

www.dbonline.jp


登録時に失敗してもスルーしてくれます。
ユニーク制約カラムであれば、重複データの登録は行わずに終了します。
なので特に気にせずにガンガンデータを作って登録ができるのです。

そうなると1000件分(希望の件数)登録できたかどうかを判定する方法が必要です。

INSERT IGNORE INTOを使って連続登録

先ほどのサンプルソースを基本としてINSERT IGNORE INTOを使ってコードを書いてみます。

<?php
//トランザクション開始
$pdo->beginTransaction();
//INSERTクエリ
$insert_sql = "INSERT IGNORE INTO t_code (code, created) VALUES (:code, CURRENT_TIMESTAMP)";
$stmt = $pdo->prepare($insert_sql);
$stmt->bindParam(':code', $code, PDO::PARAM_STR);

//実行
$flg = $stmt->execute();
//変更できたレコード数を取得
$res_count = $stmt->rowCount();
if ($res_count > 0) {
	//コミット
	$pdo->commit();
	//登録成功件数をカウント
	$insert_count++;
}

$stmt->rowCount();
変更できたレコード数を取得し、0件以上のレコードが更新されていればコミットするようなロジックです。



どちらの方法もSELECTして件数を見る必要がなく、どんどん登録していくので効率良く処理ができます。