2026年6月23日

2026年6月23日

WordPressの$wpdbでSQLを実行する方法|get_results・insert・prepareの使い方

WordPress のデータベースは、グローバル変数 $wpdb(wpdb クラスのオブジェクト)から操作できます。投稿やユーザーなら WP_Query や get_posts()、get_user_meta() などの関数を使うのが基本ですが、プラグインで作った独自のテーブルを読み書きするときや、集計のように関数では書きにくい処理では、$wpdb で SQL を直接実行します。

この記事のメソッドと戻り値は、WordPress の公式リファレンス(developer.wordpress.org の wpdb クラス)と wp-includes/class-wpdb.php で確認し、コード例は WordPress 7.1 で動作を確かめています(2026年10月時点)。

$wpdb を使う前に

関数の中では global 宣言が必要

$wpdb はグローバル変数なので、関数やメソッドの中で使うときは global $wpdb; を書きます。テーマの functions.php やプラグインのファイルは WordPress が読み込んだ後に動くため、そのまま使えます。

<?php
function myplugin_count_published_posts() {
	global $wpdb;

	return (int) $wpdb->get_var(
		$wpdb->prepare(
			"SELECT COUNT(*) FROM {$wpdb->posts} WHERE post_type = %s AND post_status = %s",
			'post',
			'publish'
		)
	);
}

テーブル名は wp_ と直接書かない

テーブル名の接頭辞(wp_ の部分)は wp-config.php の $table_prefix でサイトごとに変えられます。wp_posts のように直接書くと、接頭辞が違うサイトでは動きません。

書き方 意味
$wpdb->posts、$wpdb->postmeta、$wpdb->users、$wpdb->options など WordPress 標準のテーブル名(接頭辞付き)
$wpdb->prefix . 'my_table' 自分で作ったテーブル名(マルチサイトでは今のサイトの接頭辞になる)
$wpdb->base_prefix . 'my_table' マルチサイトで、全サイト共通のテーブルを使う場合

データを読み取る

読み取りには、結果の形に合わせて4つのメソッドを使い分けます。

メソッド 返すもの 使う場面
get_results( $sql, $output ) 全行の配列 一覧を取る
get_row( $sql, $output ) 1行だけ ID で1件を取る
get_col( $sql ) 1列分の値の配列 ID の一覧だけ欲しい
get_var( $sql ) 1つの値 件数や合計を取る

$output には次の定数を指定できます。省略すると OBJECT です。

  • OBJECT: 各行をオブジェクトで返す($row->title で読む)
  • ARRAY_A: 各行を列名がキーの連想配列で返す($row['title'])
  • ARRAY_N: 各行を番号の配列で返す
  • OBJECT_K: get_results() のみ。1列目の値をキーにした配列で返す

例として、独自テーブル {接頭辞}myplugin_items(列は id、title、price、created_at)を読みます。

<?php
global $wpdb;
$table = $wpdb->prefix . 'myplugin_items';

// 価格が 1000 以上の行を新しい順に 10 件(連想配列で受け取る)
$rows = $wpdb->get_results(
	$wpdb->prepare(
		"SELECT id, title, price FROM %i WHERE price >= %d ORDER BY created_at DESC LIMIT %d",
		$table,
		1000,
		10
	),
	ARRAY_A
);

foreach ( $rows as $row ) {
	echo esc_html( $row['title'] ) . ':' . (int) $row['price'] . "円\n";
}

// ID が 5 の行を 1 件(オブジェクトで受け取る)
$item = $wpdb->get_row(
	$wpdb->prepare( "SELECT * FROM %i WHERE id = %d", $table, 5 )
);
if ( null === $item ) {
	// 該当する行が無い
}

// 行数だけを取る
$total = (int) $wpdb->get_var( "SELECT COUNT(*) FROM {$table}" );

get_row() と get_var() は、該当する行が無いと null を返します。get_results() は、行が無いときは空の配列を返します。

データを書き込む(insert・update・delete)

1行の追加・更新・削除には専用のメソッドがあります。値は自動でエスケープされるので、prepare() は使いません。3番目以降の引数で、値の型を %s(文字列)、%d(整数)、%f(小数)で指定します。順番は渡した配列の順番と同じです。

追加: insert

<?php
global $wpdb;
$table = $wpdb->prefix . 'myplugin_items';

$result = $wpdb->insert(
	$table,
	array(
		'title'      => 'サンプル商品',
		'price'      => 1980,
		'created_at' => current_time( 'mysql' ),
	),
	array( '%s', '%d', '%s' )
);

if ( false === $result ) {
	error_log( 'insert に失敗: ' . $wpdb->last_error );
} else {
	$new_id = $wpdb->insert_id; // AUTO_INCREMENT で振られた ID
}

insert() は追加した行数(通常は 1)を、失敗すると false を返します。追加した行の ID は直後の $wpdb->insert_id で取れます。

更新: update

<?php
$updated = $wpdb->update(
	$table,
	array( 'price' => 2480 ),   // 変更する列と値
	array( 'id' => $new_id ),   // WHERE の条件
	array( '%d' ),              // 変更する値の型
	array( '%d' )               // 条件の値の型
);

if ( false === $updated ) {
	error_log( 'update に失敗: ' . $wpdb->last_error );
} elseif ( 0 === $updated ) {
	// エラーではないが、変わった行が無い(条件に合う行が無い、または同じ値だった)
}

update() は変更した行数を返します。0 はエラーではないので、失敗の判定は false === $updated のように厳密に比べます。

削除: delete

<?php
$deleted = $wpdb->delete( $table, array( 'id' => $new_id ), array( '%d' ) );

update() と delete() の条件は、配列の要素がすべて AND でつながり、= で比べます。IN や < などの条件が必要なときは、次の query() と prepare() で書きます。

任意の SQL を実行する: query

query() は、どんな SQL でも実行できます。戻り値は次のとおりです。

  • SELECT・INSERT・UPDATE・DELETE など: 対象になった行数
  • CREATE・ALTER・TRUNCATE・DROP: true
  • エラー: false
<?php
global $wpdb;
$table = $wpdb->prefix . 'myplugin_items';

// 作成から 90 日を過ぎた行をまとめて削除する
$count = $wpdb->query(
	$wpdb->prepare(
		"DELETE FROM %i WHERE created_at < %s",
		$table,
		gmdate( 'Y-m-d H:i:s', time() - 90 * DAY_IN_SECONDS )
	)
);

query() で SELECT を実行した場合、結果の行は $wpdb->last_result に入りますが、読み取りは前の節の get_results() などを使うほうが分かりやすくなります。

prepare で値を安全に埋め込む

利用者の入力やリクエストの値を SQL に入れるときは、文字列を連結せず、prepare() のプレースホルダーを使います。連結すると SQL インジェクションの原因になります。

<?php
// 悪い例: 入力値をそのまま連結している
$rows = $wpdb->get_results( "SELECT * FROM {$table} WHERE title = '" . $_GET['q'] . "'" );

// よい例: プレースホルダーで渡す
$q    = isset( $_GET['q'] ) ? sanitize_text_field( wp_unslash( $_GET['q'] ) ) : '';
$rows = $wpdb->get_results(
	$wpdb->prepare( "SELECT * FROM %i WHERE title = %s", $table, $q )
);

prepare() の決まりは次のとおりです。

  • プレースホルダーは %s(文字列)、%d(整数)、%f(小数)、%i(テーブル名・列名などの識別子。WordPress 6.2 以降)。
  • %s を引用符で囲まない('%s' ではなく %s)。引用符は prepare() が付けます。
  • SQL の中に % の文字そのものを書くときは %% と書く(例: DATE_FORMAT(created_at, '%%Y-%%m'))。
  • 値は1つずつ引数で渡すか、配列1つにまとめて渡す(混ぜない)。
  • prepare() は SQL を実行しません。返ってきた文字列を get_results() や query() に渡します。

LIKE で部分一致を検索する

LIKE の % は SQL 文に直接書かず、値の側に付けて渡します。利用者の入力に含まれる % や _ がワイルドカードとして働かないよう、先に esc_like() を通します。

<?php
$like = '%' . $wpdb->esc_like( $q ) . '%';
$rows = $wpdb->get_results(
	$wpdb->prepare( "SELECT id, title FROM %i WHERE title LIKE %s", $table, $like )
);

IN 句に配列を渡す

IN ( ... ) に複数の値を渡すときは、値の数だけプレースホルダーを作ります。

<?php
$ids          = array( 3, 8, 15 );
$placeholders = implode( ', ', array_fill( 0, count( $ids ), '%d' ) );

$rows = $wpdb->get_results(
	$wpdb->prepare(
		"SELECT id, title FROM %i WHERE id IN ( {$placeholders} )",
		array_merge( array( $table ), $ids )
	)
);

$ids が空の配列だと IN () となって SQL の構文エラーになるので、空のときは実行しないようにします。

エラーを確かめる

$wpdb は SQL のエラーで例外を投げません。WordPress はデータベースに接続するときに mysqli_report( MYSQLI_REPORT_OFF ) を実行しているため、PHP 8.1 以降でも try / catch ではエラーを捕まえられません。戻り値が false かどうかを見て、次のプロパティで内容を確かめます。

プロパティ 内容
$wpdb->last_error 直前のクエリのエラーメッセージ(エラーが無ければ空文字)
$wpdb->last_query 直前に実行した SQL
$wpdb->insert_id 直前の insert() などで振られた ID
$wpdb->rows_affected 直前の書き込みで変わった行数
<?php
$result = $wpdb->query( $sql );
if ( false === $result ) {
	error_log( sprintf( 'SQL エラー: %s / SQL: %s', $wpdb->last_error, $wpdb->last_query ) );
}

wp-config.php で WP_DEBUG と WP_DEBUG_DISPLAY がどちらも true のときは、データベースのエラーが画面にも表示されます。本番サイトでは画面に出さず、WP_DEBUG_LOG でログに書き出す設定にします。

独自テーブルを作る・インデックスを付ける

独自テーブルは、プラグインを有効化したときに dbDelta() で作るのが WordPress の標準的な方法です。インデックスも CREATE TABLE の中に書いておけば一緒に作られ、後から列やインデックスを足したときも、同じ関数で差分が反映されます。

<?php
/**
 * Plugin Name: My Plugin Items
 */

register_activation_hook( __FILE__, 'myplugin_create_table' );

function myplugin_create_table() {
	global $wpdb;

	$table           = $wpdb->prefix . 'myplugin_items';
	$charset_collate = $wpdb->get_charset_collate();

	$sql = "CREATE TABLE {$table} (
		id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
		title varchar(191) NOT NULL DEFAULT '',
		price int(11) NOT NULL DEFAULT 0,
		created_at datetime NOT NULL,
		PRIMARY KEY  (id),
		KEY price (price),
		KEY created_at (created_at)
	) {$charset_collate};";

	require_once ABSPATH . 'wp-admin/includes/upgrade.php';
	dbDelta( $sql );
}

dbDelta() は SQL の書き方に厳しく、公式ドキュメント(Plugin Handbook の Creating Tables with Plugins)では次の書き方が求められています。

  • 列の定義は1行に1つずつ書く
  • PRIMARY KEY と (id) の間は半角スペースを2つ空ける
  • インデックスは INDEX ではなく KEY と書き、KEY 名前 (列名) の形にする
  • 列名を引用符やバッククォートで囲まない
  • 列の型は小文字、CREATE TABLE などの SQL のキーワードは大文字で書く
  • 長さを指定できる型には長さを書く(例: int(11))

テーブルの変更をバージョンごとに管理する方法は、WordPressのカスタムテーブルをSCHEMA_VERSIONで管理する方法で説明しています。

使うときの注意

  • 先に標準の関数で書けないか確かめる: 投稿・メタデータ・オプションを $wpdb で直接書き換えると、オブジェクトキャッシュが更新されず、save_post などのフックも動きません。標準テーブルへの書き込みは wp_update_post() や update_post_meta() を使います。
  • DELETE や UPDATE は件数を確かめてから実行する: 同じ条件で SELECT COUNT(*) を実行し、対象の行数を確かめてから書き換えます。本番では事前にデータベースをバックアップします。
  • 重いクエリの結果は保存して使い回す: $wpdb は結果をリクエストをまたいで保存しません。集計のように時間がかかる結果は、set_transient() や wp_cache_set() で保存しておくと、毎回の実行を避けられます。
  • 出力するときはエスケープする: データベースから取り出した値を HTML に出すときは esc_html() や esc_attr() を通します。prepare() は SQL 用のエスケープで、HTML 用ではありません。

まとめ

  • 関数の中では global $wpdb; を書き、テーブル名は $wpdb->posts や $wpdb->prefix . 'テーブル名' で組み立てる
  • 読み取りは get_results()・get_row()・get_col()・get_var()、1行の書き込みは insert()・update()・delete()、それ以外は query()
  • 外から来た値は prepare() のプレースホルダー(%s・%d・%f・%i)で渡す。LIKE は esc_like()、IN は値の数だけプレースホルダーを作る
  • エラーは例外では来ない。戻り値の false と $wpdb->last_error で確かめる
  • 独自テーブルとインデックスは、有効化時に dbDelta() で作る

関連記事

お気軽にご相談ください

お見積りへ お問い合わせへ
▲