Database, SQL, PDO, and WordPress wpdb
WordPress Data API Preference
| Need | Prefer |
|---|---|
| Query posts | WP_Query, get_posts() |
| Post fields | get_post(), template tags |
| Post meta | get_post_meta(), update_post_meta() |
| User meta | get_user_meta(), update_user_meta() |
| Term meta | get_term_meta(), update_term_meta() |
| Site settings | get_option(), update_option() |
| Temporary cache | get_transient(), set_transient() |
| Custom SQL | $wpdb |
WP_Query and get_posts
$query = new WP_Query([
'post_type' => 'book',
'post_status' => 'publish',
'posts_per_page' => 10,
]);
$ids = get_posts([
'post_type' => 'book',
'fields' => 'ids',
'posts_per_page' => 20,
]);
Use wp_reset_postdata() after secondary loops.
Meta and Options
$value = get_post_meta($post_id, '_book_isbn', true);
update_post_meta($post_id, '_book_isbn', $isbn);
delete_post_meta($post_id, '_book_isbn');
$settings = get_option('myplugin_settings', []);
update_option('myplugin_settings', $settings, false);
delete_option('myplugin_settings');
Avoid autoloading large option data.
Transients
$items = get_transient('myplugin_items');
if (false === $items) {
$items = myplugin_fetch_items();
set_transient('myplugin_items', $items, 10 * MINUTE_IN_SECONDS);
}
wpdb Basics
global $wpdb;
$table = $wpdb->prefix . 'myplugin_logs';
Prepared Select
$row = $wpdb->get_row(
$wpdb->prepare(
"SELECT * FROM {$table} WHERE id = %d",
$id
),
ARRAY_A
);
get_var, get_row, get_results
$count = $wpdb->get_var("SELECT COUNT(*) FROM {$wpdb->posts}");
$row = $wpdb->get_row($sql, ARRAY_A);
$rows = $wpdb->get_results($sql, ARRAY_A);
Insert, Update, Delete
$wpdb->insert(
$table,
['user_id' => $user_id, 'event_name' => $event_name],
['%d', '%s']
);
$wpdb->update(
$table,
['event_name' => $event_name],
['id' => $id],
['%s'],
['%d']
);
$wpdb->delete($table, ['id' => $id], ['%d']);
prepare Placeholders
| Placeholder | Type |
|---|---|
%s | String |
%d | Integer |
%f | Float |
%i | Identifier in supported WordPress versions |
Custom Tables and dbDelta
require_once ABSPATH . 'wp-admin/includes/upgrade.php';
dbDelta($sql);
PDO Outside WordPress
$pdo = new PDO($dsn, $user, $password, [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
]);
$stmt = $pdo->prepare('SELECT * FROM users WHERE email = :email');
$stmt->execute(['email' => $email]);
$row = $stmt->fetch(PDO::FETCH_ASSOC);
Inside WordPress, use $wpdb unless there is a strong reason not to.
Performance Notes
- Request only needed fields.
- Avoid slow meta queries on large sites.
- Add indexes for custom tables based on real query patterns.
- Cache expensive computed values.
- Avoid running write queries on every page load.
- Use Query Monitor during development.
Common Pitfalls
- Using raw SQL when
WP_Queryis enough. - Forgetting
$wpdb->prepare(). - Using post meta for high-volume relational data.
- Creating custom tables without schema versioning.
- Storing large autoloaded options.
- Forgetting multisite prefixes.