結論
$wpdb->insert() と $wpdb->update() の戻り値は、成功なら影響を受けた行数(int)、失敗なら false です。ここで重要なのは、update() が返す 0 は失敗ではないことです。「該当行が無い」場合だけでなく、「該当行はあったが、既に同じ値だったので変更が不要だった」場合も 0 になります。
したがって、成否の判定は必ず厳密比較で書きます。
$result = $wpdb->update( $table, $data, $where );
if ( false === $result ) {
// エラー: $wpdb->last_error を確認
}
if ( ! $result ) と書くと、正常な「0行更新」をエラー扱いしてしまいます。
原因・仕組み
公式リファレンスでは、wpdb::insert( $table, $data, $format ) は挿入した行数か false を返し、直前の挿入 ID は $wpdb->insert_id で得られます。$format を省略すると、値は文字列として扱われます(wpdb が知っている列型があればそれが優先)。また、insert() や update() は値を自動でエスケープするため、prepare() は不要です。
update( $table, $data, $where, $format, $where_format ) は、更新した行数または false を返します。公式リファレンスは、0 が失敗を意味しないこと、そして false === $updated で厳密に比較することを明記しています。
手順(サンプルコード)
global $wpdb;
$table = $wpdb->prefix . 'my_log';
$ok = $wpdb->insert(
$table,
array( 'msg' => $message, 'n' => $count ),
array( '%s', '%d' ) // data と同じ並び・同じ個数で指定
);
if ( false === $ok ) {
error_log( 'insert failed: ' . $wpdb->last_error );
return new WP_Error( 'db_insert', 'ログの保存に失敗しました。' );
}
$new_id = (int) $wpdb->insert_id;
$updated = $wpdb->update(
$table,
array( 'n' => $count + 1 ),
array( 'id' => $new_id ),
array( '%d' ),
array( '%d' )
);
if ( false === $updated ) {
error_log( 'update failed: ' . $wpdb->last_error );
} elseif ( 0 === $updated ) {
// 成功だが、変更なし(同じ値、または該当行なし)
}
「行が存在するか」を厳密に知りたいときは、別途 SELECT で確認します。INSERT ... ON DUPLICATE KEY UPDATE が必要な場合は、prepare() を使った query() で書きます。
動作確認(検証環境と結果)
WordPress 7.1.2(ja)/PHP 8.2.12/MariaDB 10.4.32。検証用の一時テーブル(id, msg varchar(10), n int)を作って実行し、最後に削除しました(tests フォルダの p12.php)。
insert: 1 id=1
bad column insert: false | err: Unknown column 'nope' in 'field list'
update same value: 0
update changed: 1
update no match: 0
update bad column: false | err: Unknown column 'zzz' in 'field list'
too long: false | err: ... 次のフィールドの値の処理に失敗しました: msg。...
delete: 1
- 存在しない列を指定すると、
insertもupdateもfalseを返し、last_errorに MySQL のエラーが入りました。 - 同じ値での
updateと、該当行が無いupdateは、どちらも0でした。区別はできません。 - varchar(10) の列に30文字を入れようとすると、
wpdbが事前に検知してfalseを返しました。この場合、last_queryは直前のクエリのままで、実際の INSERT は送信されていませんでした。 $wpdb->show_errors( false )にしていない場合は、画面にエラー文が出力されます。
$wpdb->insert・update の戻り値とエラーの確認方法の注意点
last_errorは、直後に読みます。間に別のクエリを実行すると上書きされます。$formatの長さは$dataの個数と一致させます。wpdb::process_field_formats()のソースを見ると、足りない分には最初の書式が使われるため、エラーにならずに意図しない型(例: 文字列を%dで整数化)で保存されることがあります。- 本番では
show_errors()を有効にせず、ログに出力します。 insert_idは AUTO_INCREMENT 列を持つテーブルで、直前の INSERT に対する値です。複数の処理が並行しても、接続ごとの値なので混ざりません。NULLを入れたい場合は、値にnullを渡します。$formatに関わらず NULL になると公式にあります。- テーブルの作成・変更は、
dbDelta()を使うのが基本です。
$wpdb->insert・update の戻り値とエラーの確認方法でよくあるミス
if ( $wpdb->update(...) )と書き、変更が無いだけの更新を失敗扱いにする。if ( $wpdb->insert(...) == false )のように緩い比較を使う(0 == falseは真)。$wpdb->prepare()をinsert()にも付けて、二重にエスケープする。$wpdb->insert_idを失敗後にも参照し、前回の値を使ってしまう。- ユーザーが送信した配列を、そのまま
$dataに渡して、意図しない列を更新させる。キーを許可リストで絞ります。
$wpdb->insert・update の戻り値とエラーの確認方法のチェックリスト
- 戻り値を
false ===で比較している - エラー時に
last_errorをログに残している $formatの個数が$dataと一致している- 更新対象の列名を許可リストで絞っている
- 本番で
show_errors()を有効にしていない
$wpdb->insert・update の戻り値とエラーの確認方法のFAQ(よくある質問)
Q. update の結果で、該当行が無かったことを知るには。
A. 事前に SELECT で確認するか、insert と update を使い分けるロジックを組みます。
Q. $wpdb->replace() との違いは。
A. 主キーが重複した場合、既存行を削除してから挿入します。部分更新には向きません。
Q. 複数行を一括で挿入したい。
A. prepare() で VALUES を組み立てる query() を使います。IN 句と同様、プレースホルダを動的に作ります。
筆者の見解($wpdb->insert・update の戻り値とエラーの確認方法)
0 と false の区別は、小さな違いですが、運用中の障害調査で効いてくると考えています。私見では、$wpdb を呼ぶ箇所を小さなラッパー関数にまとめ、失敗時に WP_Error を返す形に統一しておくと、呼び出し側の分岐も読みやすくなります。
$wpdb->insert・update の戻り値とエラーの確認方法の関連項目
- $wpdb->insert() の使い方|説明・引数・注意点
- $wpdb->update() の使い方|説明・引数・注意点
- $wpdb->prepare() の使い方|説明・引数・注意点
- $wpdb->query() の使い方|説明・引数・注意点
- dbDelta() の使い方|説明・引数・注意点

