wp_postmeta

Key-value storage for post metadata. Extends wp_posts with arbitrary custom fields.

Schema

ColumnTypeNullDefaultDescription
meta_idbigint(20) unsignedNOauto_incrementUnique identifier for the meta entry
post_idbigint(20) unsignedNO0Associated post ID (FK → wp_posts.ID)
meta_keyvarchar(255)YESNULLMeta key name
meta_valuelongtextYESNULLMeta value (can be serialized)

Indexes

Key NameColumnsUniquePurpose
PRIMARYmeta_idYesPrimary key
post_idpost_idNoAll meta for a post
meta_keymeta_key(191)NoLookup by key name

Foreign Key Relationships

  • post_idwp_posts.ID (the post this meta belongs to)

Common Meta Keys

Core WordPress

KeyDescription
_thumbnail_idFeatured image attachment ID
_wp_attached_fileRelative path to attachment file
_wp_attachment_metadataSerialized attachment data (sizes, dimensions)
_wp_attachment_image_altAlt text for images
_edit_lockUser currently editing (user_id:timestamp)
_edit_lastLast user who edited
_wp_page_templatePage template file
_wp_old_slugPrevious slugs for redirects
_wp_old_datePrevious publish dates
_enclosemeFlag for enclosure processing
_menu_item_*Navigation menu item settings

Custom Fields (no underscore prefix)

User-defined custom fields appear in the post editor. Keys starting with _ are hidden from the custom fields UI.

Serialized Values

Many meta values store serialized PHP arrays:

php
// _wp_attachment_metadata example
a:5:{
  s:5:"width";i:1920;
  s:6:"height";i:1080;
  s:4:"file";s:25:"2024/01/my-image.jpg";
  s:5:"sizes";a:4:{...}
  s:10:"image_meta";a:12:{...}
}

Use maybe_unserialize() when reading, maybe_serialize() when writing.

Common Queries

Get all meta for a post

sql
SELECT meta_key, meta_value 
FROM wp_postmeta 
WHERE post_id = 123;

Get specific meta value

sql
SELECT meta_value 
FROM wp_postmeta 
WHERE post_id = 123 
  AND meta_key = '_thumbnail_id';

Get posts with specific meta

sql
SELECT p.* 
FROM wp_posts p
JOIN wp_postmeta pm ON p.ID = pm.post_id
WHERE pm.meta_key = 'featured' 
  AND pm.meta_value = '1'
  AND p.post_status = 'publish';

Get posts ordered by meta value

sql
SELECT p.*, pm.meta_value as price
FROM wp_posts p
JOIN wp_postmeta pm ON p.ID = pm.post_id
WHERE pm.meta_key = 'price'
  AND p.post_type = 'product'
  AND p.post_status = 'publish'
ORDER BY CAST(pm.meta_value AS DECIMAL) DESC;

Count posts by meta key

sql
SELECT meta_key, COUNT(*) as count 
FROM wp_postmeta 
GROUP BY meta_key 
ORDER BY count DESC 
LIMIT 20;

Delete orphaned postmeta

sql
DELETE pm FROM wp_postmeta pm
LEFT JOIN wp_posts p ON pm.post_id = p.ID
WHERE p.ID IS NULL;

WordPress API Functions

  • get_post_meta( $post_id, $key, $single ) – Get meta value(s)
  • add_post_meta( $post_id, $key, $value, $unique ) – Add meta
  • update_post_meta( $post_id, $key, $value ) – Update meta
  • delete_post_meta( $post_id, $key, $value ) – Delete meta
  • get_post_custom() – Get all custom fields
  • get_post_custom_keys() – Get all meta keys for a post

Performance Notes

  • Multiple values per key allowed (use $single = false in get_post_meta())
  • Index on meta_key is limited to 191 characters
  • Large serialized values can bloat the table
  • Consider dedicated tables for high-volume custom data