• WordPress uses a MySQL (or MariaDB) database. By default, it consists of 12 core tables. The 12 core tables are:

  • wp_commentmeta: Stores extra information (metadata) about comments.

  • wp_comments: Contains all comments on posts and pages.

  • wp_links: Stores information related to the Links feature (often deprecated but still included for backward compatibility).

  • wp_options: Stores general site settings, configuration options, and temporary data (transients) for themes and plugins.(Site URL, Admin email, active plugins, and theme configurations).

  • wp_postmeta: Stores metadata for posts, pages, and custom post types (e.g., custom fields).

  • wp_posts: The core table that stores the content itself, including posts, pages, and navigation menu items.

  • wp_terms: Stores categories, tags, and other taxonomies for posts and links.

  • wp_termmeta: Stores metadata for terms.

  • wp_term_relationships: Manages the associations between items in the wp_posts table and terms in the wp_terms table.

  • wp_term_taxonomy: Defines the taxonomy (category, tag, etc.) for entries in the wp_terms table.

  • wp_usermeta: Stores metadata and extra information about users.

  • wp_users: Contains the list of registered users on the website


  • The wpdb Class is the “database driver.” It handles the actual connection and prevents security issues like SQL Injection.
global $wpdb;
$results = $wpdb->get_results( "SELECT * FROM {$wpdb->prefix}posts WHERE post_status = 'publish'" );
  • High-Level API Functions: Most developers use built-in functions that handle the SQL for them:
    • get_post($id): Grabs a row from wp_posts.
    • update_option('name', 'value'): Changes a row in wp_options.
    • get_user_meta($user_id, 'key'): Grabs data from wp_usermeta.

WP_Query is a PHP class that queries the WordPress database and returns posts based on rules you define.

$args = [
    'post_type' => 'post',
    'posts_per_page' => 5   // -1 for all posts
];

$query = new WP_Query( $args );

wp_postmeta table

ColumnData TypeKeyDescription
meta_idbigint(20) unsignedPRIThe unique identifier for each metadata row (Auto-increment).
post_idbigint(20) unsignedMULThe ID of the post this metadata belongs to (Foreign key to wp_posts.ID).
meta_keyvarchar(255)MULThe name or “key” of the metadata (e.g., _wp_page_template).
meta_valuelongtextThe actual value of the metadata. Often stores serialized arrays.
  • Hidden vs. Visible Meta
    • Public Meta: Keys that do not start with an underscore (e.g., my_custom_field) are usually visible in the WordPress admin dashboard under “Custom Fields.”
    • Protected Meta: Keys starting with an underscore (e.g., _edit_last or _thumbnail_id) are hidden from the standard UI and are intended for internal plugin/theme use.

The meta_value column is a longtext type, allowing it to store massive amounts of data.

Note: Because meta_value is often serialized or contains long text, searching through it using SQL LIKE queries can be very slow on large databases because it cannot be efficiently indexed.


  • post id + meta key cannot be made composite key, since same can appear mulitple times.
// Adding multiple features to the same post ID
add_post_meta(500, 'feature', 'Pool');
add_post_meta(500, 'feature', 'Garage');

// Returns an array: ['Pool', 'Garage']
$features = get_post_meta(500, 'feature', false);

// Returns only the first string: 'Pool'
$price = get_post_meta(500, '_price', true);

# You can allow only one value:
add_post_meta( $post_id, 'price', '19.99', true );

# If it doesn’t exist → it creates it
update_post_meta( $post_id, 'price', '24.99' );

meta_query and tax_query are arguments passed to WP_Query that let you filter posts by:

  • meta_query → post meta (custom fields)
  • tax_query → taxonomies (categories, tags, custom taxonomies)
$args = [
    'post_type' => 'post',
    'tax_query' => [
        [
            'taxonomy' => 'category',
            'terms' => 'products'
        ]
    ],
    'meta_query' => [
        [
            'key' => 'price',
            'value' => 100,
            'compare' => '<',
            'type' => 'NUMERIC'
        ]
    ]
];

$query = new WP_Query( $args );
  • $post is a global PHP variable in WordPress.
  • It represents the current post object being processed in “The Loop.”
  • Type: WP_Post object (class representing a single post in the database).
if ( $query->have_posts() ) {
    while ( $query->have_posts() ) {
        $query->the_post();  // Sets the global $post to the current post
        ?>
        <h2><?php the_title(); ?></h2>
        <div><?php the_content(); ?></div>
        <?php
    }
}
wp_reset_postdata();
// After a custom query loop, this restores the global $post to what it was before the loop (usually the main query).