Close Menu
GeekBlog

    Subscribe to Updates

    Get the latest creative news from FooBar about art, design and business.

    What's Hot

    New York vs Florida: Which State Is Better to Move To?

    July 30, 2026

    What State Is Best to Invest in Real Estate in 2026?

    July 30, 2026

    Texas vs California: Which State Is Better in 2026?

    July 30, 2026
    Facebook X (Twitter) Instagram Threads
    GeekBlog
    • Home
    • Mobile
    • Tech News
    • Blog
    • How-To Guides
    • AI & Software
    Facebook
    GeekBlog
    Home»Blog»How to Run a Database Query in WordPress (4 Safe Methods)
    Blog

    How to Run a Database Query in WordPress (4 Safe Methods)

    Ethan CaldwellBy Ethan CaldwellJuly 30, 202613 Mins Read
    Share Facebook Twitter Pinterest LinkedIn Tumblr Email Copy Link
    Share
    Facebook Twitter LinkedIn Pinterest Email Copy Link

    There are four reliable ways to run a database query in WordPress: phpMyAdmin in your hosting control panel, WP-CLI’s wp db query command, the $wpdb class inside PHP, and a plugin that gives you a SQL console in wp-admin. Which one you pick depends on whether you need a one-off lookup or repeatable code, and on whether your host gives you SSH.

    Quick answer: For a one-off read, open phpMyAdmin from the Databases group in cPanel, select your site’s database, and use the SQL tab. For anything scripted or repeated, use WP-CLI: wp db query "SELECT COUNT(*) FROM wp_posts;". Inside a plugin or theme, always use $wpdb with prepare(), never string concatenation. And back up the database before any query that writes.

    Two things before you start. Know your table prefix, which is set by $table_prefix in wp-config.php and is very often not wp_. And know that WordPress will not save you from a bad query. There is no undo button, no confirmation dialog, and no version history. The database does exactly what you tell it to.

    Step 1: Back up, then test on staging

    This is not boilerplate. A mistyped DELETE takes two seconds and can wipe every post on the site.

    wp db export backup-before-query.sql

    In cPanel without SSH: open phpMyAdmin, select the database in the left sidebar, click the Export tab, keep the Quick method and SQL format, and download the file. Do not skip this because the query “looks fine”. Everyone’s fatal query looked fine.

    Better still, run it on a copy first. A local install takes about ten minutes to set up, and our guide to installing WordPress on a local host covers importing a production database into it. Test there, confirm the row count matches what you expect, then run it for real.

    Heads up: Never run an UPDATE or DELETE without a WHERE clause. DELETE FROM wp_posts; empties your entire site. Write the query as a SELECT first, check the rows it returns are the ones you meant, and only then swap the SELECT ... for DELETE or UPDATE ... SET, leaving the WHERE untouched.

    Method 1: phpMyAdmin in cPanel

    phpMyAdmin sits in the Databases group of cPanel, alongside MySQL Databases and the Database Wizard. It is the right tool when you want to look at data, run a single query, and get out.

    1. Open phpMyAdmin and pick your database from the left sidebar. If several are listed, match the name against DB_NAME in wp-config.php.
    2. Click the SQL tab.
    3. Paste your query and click Go.

    Read results in the grid, use Export on the results to pull them out as CSV, and use the Search tab on an individual table when you would rather click than type. The downsides: it times out on large tables, it is easy to run something against the wrong database, and there is no dry-run mode.

    Method 2: WP-CLI

    If your host offers SSH, or cPanel’s Terminal in the Advanced group, this is the best of the four. WP-CLI reads credentials straight from wp-config.php, so there is nothing to configure and no chance of hitting the wrong database.

    wp db query "SELECT COUNT(*) FROM wp_posts WHERE post_status = 'publish';"
    
    wp db query "SELECT option_value FROM wp_options WHERE option_name = 'home';" --skip-column-names
    
    wp db query < cleanup.sql
    
    wp db tables
    wp db size --tables
    wp db check

    The file form is the one worth remembering. Write your statements into a .sql file, review it, keep it in version control, and pipe it in. That gives you a record of exactly what you ran.

    WP-CLI also has purpose-built commands that are safer than raw SQL for the jobs people most often write raw SQL for: wp search-replace, wp option, wp post meta and wp transient. Reach for those first.

    Method 3: the $wpdb class in PHP

    $wpdb is the global database object WordPress uses internally. This is the only correct way to query the database from inside a plugin, a theme, or a snippet.

    Reading data

    global $wpdb;
    
    // Many rows, as an array of objects.
    $rows = $wpdb->get_results(
        $wpdb->prepare(
            "SELECT ID, post_title FROM {$wpdb->posts}
             WHERE post_type = %s AND post_status = %s
             ORDER BY post_date DESC LIMIT %d",
            'post',
            'publish',
            20
        )
    );
    
    // One value.
    $pending = (int) $wpdb->get_var(
        "SELECT COUNT(*) FROM {$wpdb->comments} WHERE comment_approved = '0'"
    );
    
    // One row, as an associative array.
    $user = $wpdb->get_row(
        $wpdb->prepare( "SELECT * FROM {$wpdb->users} WHERE user_email = %s", $email ),
        ARRAY_A
    );
    
    // One column, as a flat array.
    $ids = $wpdb->get_col( "SELECT ID FROM {$wpdb->posts} WHERE post_parent = 0" );

    Note the table names. $wpdb->posts, $wpdb->postmeta, $wpdb->users and friends are properties that already include the prefix. For your own tables, build the name with $wpdb->prefix . 'my_table'. Hardcoding wp_posts is the single most common reason a plugin breaks on someone else’s site.

    Recommended for you:

    Installing Elasticsearch on Cloudways (Complete Guide)
    Blog·Jul 30, 2026

    Installing Elasticsearch on Cloudways (Complete Guide)

    prepare() and why unprepared interpolation is a security hole

    Dropping a variable straight into a query string is a SQL injection vulnerability. Not “bad practice” or “risky in theory”: a working exploit.

    // NEVER. A crafted ?u= value can read or destroy your database.
    $rows = $wpdb->get_results(
        "SELECT * FROM {$wpdb->users} WHERE user_login = '" . $_GET['u'] . "'"
    );
    
    // Correct.
    $rows = $wpdb->get_results(
        $wpdb->prepare( "SELECT * FROM {$wpdb->users} WHERE user_login = %s", $_GET['u'] )
    );

    The placeholders are %s for strings, %d for integers, %f for floats, and %i for identifiers such as table and column names. Three rules that trip people up: do not put quotes around %s yourself, because prepare() adds them; a literal percent sign has to be written as %%; and a LIKE pattern should be built with '%' . $wpdb->esc_like( $term ) . '%' passed in as a %s.

    The insert, update and delete helpers

    For writes, skip SQL entirely. These methods escape everything for you.

    $table = $wpdb->prefix . 'my_events';
    
    $wpdb->insert(
        $table,
        array( 'event' => 'import', 'qty' => 42, 'created_at' => current_time( 'mysql' ) ),
        array( '%s', '%d', '%s' )
    );
    $new_id = $wpdb->insert_id;
    
    $wpdb->update(
        $table,
        array( 'qty' => 43 ),          // data
        array( 'id' => $new_id ),      // where
        array( '%d' ),                 // data formats
        array( '%d' )                  // where formats
    );
    
    $wpdb->delete( $table, array( 'id' => $new_id ), array( '%d' ) );

    update() and delete() require a where array, so they cannot accidentally hit every row. That alone is a good reason to prefer them over hand-written SQL.

    Error handling

    $result = $wpdb->query( $sql );
    
    if ( false === $result ) {
        error_log( 'Query failed: ' . $wpdb->last_error );
        error_log( 'SQL was: ' . $wpdb->last_query );
    }

    query() returns the number of affected rows, true for schema statements, and false on error. Zero is not an error, so test with ===. During development, $wpdb->show_errors() prints problems to the screen; $wpdb->hide_errors() puts the lid back on before you deploy.

    Method 4: a plugin-based SQL runner

    If you have no SSH and no phpMyAdmin, a plugin can give you a query console inside wp-admin. WP Data Access is the most capable option currently maintained on the plugin directory, with a query builder, a data explorer, import and export, and scheduled queries. Install it, run what you need, and deactivate it afterwards.

    For the specific job of rewriting strings across the database, Better Search Replace is a safer choice than raw SQL. It handles serialized data and has a dry-run mode that reports how many fields would change before it touches anything.

    Tip: Treat any SQL-console plugin as a temporary tool, not part of your stack. Anything that can run arbitrary SQL from the browser is a serious target if an admin account is ever compromised. Deactivate and delete it when the job is done, and remember that the plugin’s own settings stay behind in the database afterwards, as our guide to where WordPress plugin settings are stored explains.

    Which method should you use?

    MethodNeedsBest forRepeatable?Risk
    phpMyAdmincPanel accessOne-off reads, browsing tables, exportsNoMedium. No dry run, wrong-database mistakes
    WP-CLISSH or cPanel TerminalScripted maintenance, migrations, bulk workYesLow. Reads config, has dry-run commands
    $wpdb in PHPAbility to deploy codeAnything shipped in a plugin or themeYesLow with prepare(), high without it
    SQL pluginwp-admin access onlyLocked-down hosting with no other routePartlyMedium. Extra attack surface

    Queries worth keeping

    Replace wp_ with your real prefix in all of these.

    Published posts with no featured image

    SELECT p.ID, p.post_title
    FROM wp_posts p
    LEFT JOIN wp_postmeta pm
           ON pm.post_id = p.ID AND pm.meta_key = '_thumbnail_id'
    WHERE p.post_type = 'post'
      AND p.post_status = 'publish'
      AND pm.post_id IS NULL
    ORDER BY p.post_date DESC;

    Find and replace inside post content

    -- Look first.
    SELECT ID, post_title FROM wp_posts
    WHERE post_content LIKE '%old-domain.com%';
    
    -- Then change it.
    UPDATE wp_posts
    SET post_content = REPLACE(post_content, 'old-domain.com', 'new-domain.com')
    WHERE post_content LIKE '%old-domain.com%';

    This is safe for plain text inside post_content. It is not safe anywhere serialized data lives, which is most of wp_options and a lot of wp_postmeta. More on that below.

    Users who have never logged in

    Worth knowing before you write this one: WordPress core does not record last login anywhere. There is no last_login column and no core usermeta key for it. Any query claiming to list inactive users is really querying a field some plugin created.

    -- Only works if a plugin writes a 'last_login' usermeta key.
    SELECT u.ID, u.user_login, u.user_email, u.user_registered
    FROM wp_users u
    LEFT JOIN wp_usermeta m ON m.user_id = u.ID AND m.meta_key = 'last_login'
    WHERE m.umeta_id IS NULL
    ORDER BY u.user_registered DESC;
    
    -- Core-only alternative: registered accounts that never published anything.
    SELECT u.ID, u.user_login, u.user_email, u.user_registered
    FROM wp_users u
    LEFT JOIN wp_posts p ON p.post_author = u.ID
    WHERE p.ID IS NULL
    ORDER BY u.user_registered DESC;

    Delete expired transients

    DELETE a, b FROM wp_options a
    JOIN wp_options b
      ON b.option_name = CONCAT('_transient_', SUBSTRING(a.option_name, 20))
    WHERE a.option_name LIKE '_transient_timeout_%'
      AND a.option_value < UNIX_TIMESTAMP();

    That deletes the timeout row and its matching value row together. The WP-CLI equivalent is one word shorter and handles site transients too: wp transient delete --expired. Use that if you can.

    Orphaned postmeta

    -- Count them first.
    SELECT COUNT(*) FROM wp_postmeta pm
    LEFT JOIN wp_posts p ON p.ID = pm.post_id
    WHERE p.ID IS NULL;
    
    -- Then remove them.
    DELETE pm FROM wp_postmeta pm
    LEFT JOIN wp_posts p ON p.ID = pm.post_id
    WHERE p.ID IS NULL;

    On a site that has been running for years this can free real space. Run the count first so you know what you are about to remove.

    Troubleshooting

    “Table ‘yourdb.wp_posts’ doesn’t exist”

    Your prefix is not wp_. Check $table_prefix in wp-config.php, or run wp db tables. Many installers randomize it to something like wp_k3f9_. On multisite, subsite tables carry the blog ID as well, so site 2’s posts live in wp_2_posts. If the message names the wrong database entirely, you are connected to the wrong one.

    The query times out or the page hangs

    Large wp_postmeta and wp_options tables are the usual cause, and phpMyAdmin gives up long before the database does. Batch it: add LIMIT 5000 and run it repeatedly until it reports zero rows affected. Better, run it through WP-CLI, which is not bound by PHP’s web request timeout. Put EXPLAIN in front of a slow SELECT to see whether it is using an index at all.

    Serialized data broke after a search and replace

    This is the classic self-inflicted wound. A serialized PHP string stores the byte length of every string inside it. Change example.com to example.org and nothing breaks, because the lengths match. Change it to my-new-site.com and every affected length prefix is now wrong, PHP cannot unserialize the value, and widgets, theme settings and plugin options quietly reset to defaults.

    The fix is to never use SQL REPLACE() on serialized columns. Use WP-CLI instead, which unserializes, replaces, and re-serializes with correct lengths:

    wp search-replace 'https://old.example.com' 'https://new.example.com' --dry-run
    wp search-replace 'https://old.example.com' 'https://new.example.com' --skip-columns=guid --report-changed-only

    Always run --dry-run first. Keep --skip-columns=guid, because post GUIDs are identifiers and rewriting them confuses feed readers. If you have already corrupted data this way, restore from the backup you took. There is no repair query.

    “Error establishing a database connection” after your query

    Recommended for you:

    Installing CakePHP on Cloudways: Step-by-Step
    Blog·Jul 30, 2026

    Installing CakePHP on Cloudways: Step-by-Step

    You did not break the connection with a SELECT. Either you dropped or renamed a table, or the query was heavy enough that the server hit a connection or memory limit and is still recovering. Wait a minute, reload, and check your host’s error log in the Metrics group of cPanel.

    You cannot get into wp-admin to install a query plugin

    Then a plugin is probably the reason. Deal with that first: our guide to disabling a WordPress plugin from cPanel gets you back into the dashboard without touching SQL.

    Frequently asked questions

    How do I run a SQL query in WordPress without a plugin?

    Use phpMyAdmin from the Databases group in cPanel, or WP-CLI over SSH with wp db query "your SQL here". Both talk to the database directly and need nothing installed inside WordPress. WP-CLI is the better choice if it is available, because it reads your credentials from wp-config.php and cannot hit the wrong database.

    Is $wpdb->query() safe to use?

    Yes, provided every variable in the SQL goes through $wpdb->prepare() first. Unprepared string concatenation is a SQL injection hole, full stop. For inserts, updates and deletes, prefer the insert(), update() and delete() helpers, which escape values for you and require a where clause.

    Where do I find my WordPress table prefix?

    Open wp-config.php in the site root and look for the $table_prefix line. It is typically wp_, but many installers randomize it. From the command line, wp db tables lists every table with the prefix already applied.

    Can I undo a database query in WordPress?

    No. There is no undo, no trash, and no revision history for direct SQL. The only way back is a backup taken before you ran it. Export the database first every time, and keep the export until you have confirmed the site still behaves correctly.

    Why did my widgets disappear after a search and replace?

    Because widget settings are stored as serialized arrays, and a raw SQL REPLACE() changed a string’s length without updating its length prefix. PHP can no longer unserialize the value, so WordPress falls back to defaults. Restore your backup and redo the replacement with wp search-replace.

    How do I query the database from inside a page template?

    Declare global $wpdb; at the top of the function, then use $wpdb->get_results() with prepare(). Before you do, check whether WP_Query, get_posts() or get_users() can do the job. They use the object cache and respect filters, so they are faster and better behaved than raw SQL.

    Wrapping up

    WP-CLI if you have shell access, phpMyAdmin if you do not, $wpdb with prepare() for anything that ships as code, and a plugin only when the first three are unavailable. That ordering will serve you well on any host.

    The two habits that matter more than the tool: export the database before every write, and write the query as a SELECT before you turn it into an UPDATE or DELETE. For reference, WordPress documents the wpdb class, the wp db query command and wp search-replace in full. If you are writing queries to audit tracking data, our guides to adding Google Analytics to WordPress and tracking custom events in Google Analytics cover the reporting side of the same problem.

    Share. Facebook Twitter Pinterest LinkedIn Tumblr Telegram Email Copy Link
    Previous ArticleInstalling CakePHP on Cloudways: Step-by-Step
    Next Article Installing Elasticsearch on Cloudways (Complete Guide)
    Ethan Caldwell

      Ethan Caldwell is GeekBlog's resident Apple specialist, covering the entire Apple ecosystem - iPhone, iPad, Mac, Apple Watch, AirPods and the software that ties them together. A longtime iOS user and gadget collector, Ethan tracks Cupertino's every move, breaking down Apple keynotes, A- and M-series chip benchmarks, iOS feature updates and the rumor mill into clear, practical takes that help readers decide whether the latest Apple hardware is worth the upgrade.

      Related Posts

      18 Mins Read

      New York vs Florida: Which State Is Better to Move To?

      15 Mins Read

      What State Is Best to Invest in Real Estate in 2026?

      15 Mins Read

      Texas vs California: Which State Is Better in 2026?

      17 Mins Read

      How to Explain Your Coding Solution in an Interview

      13 Mins Read

      Massachusetts vs Kentucky: Which State Is Better?

      12 Mins Read

      Washington vs Indiana: Which State Is Better to Live In?

      Top Posts

      Japan Skips Exams Until Age 10 and Teaches Character Instead. The Results Are Complicated.

      July 29, 20268 Views

      Husbands Cause More Stress Than Kids? Science Says Many Moms Feel Exactly That

      July 28, 20268 Views

      How to Block Twitch Ads with uBlock Origin (2026 Guide)

      June 15, 20267 Views
      Stay In Touch
      • Facebook

      Subscribe to Updates

      Get the latest tech news from FooBar about tech, design and biz.

      Most Popular

      Best Stores for Buying MP3 and Digital Music You Can Keep Forever (2026)

      August 2, 2025901 Views

      Discord will require a face scan or ID for full access next month

      February 9, 2026770 Views

      Trade in your old phone and get up to $1,100 off a new iPhone 17 at AT&T – here’s how

      September 10, 2025382 Views
      Our Picks

      New York vs Florida: Which State Is Better to Move To?

      July 30, 2026

      What State Is Best to Invest in Real Estate in 2026?

      July 30, 2026

      Texas vs California: Which State Is Better in 2026?

      July 30, 2026

      Subscribe to Updates

      Get the latest creative news from FooBar about art, design and business.

      Facebook
      • About Us
      • Contact us
      • Privacy Policy
      • Disclaimer
      • Terms and Conditions
      © 2026 GeekBlog

      Type above and press Enter to search. Press Esc to cancel.