{"id":10392,"date":"2026-09-12T07:20:00","date_gmt":"2026-09-12T05:20:00","guid":{"rendered":"https:\/\/www.lukaswojcik.com\/blog\/?p=10392"},"modified":"2026-10-26T13:49:06","modified_gmt":"2026-10-26T12:49:06","slug":"investigating-slow-wordpress-filters-how-meta_query-queries-the-database","status":"publish","type":"post","link":"https:\/\/www.lukaswojcik.com\/blog\/en\/wordpress-plugins-tricks\/investigating-slow-wordpress-filters-how-meta_query-queries-the-database\/","title":{"rendered":"Investigating Slow WordPress Filters: How meta_query Queries the Database"},"content":{"rendered":"<p>A filter with three custom fields can produce three joins to <code>wp_postmeta<\/code>. A join connects rows from tables; an alias gives one use of a table a distinct name. The number of joins depends on the conditions and their relationship, not simply on the number of fields.<\/p>\n<p>Metadata stores extra information about a post, usually as a field name and value. WP_Query builds WordPress post queries, and SQL is the database query language. An index is an additional lookup structure that helps find rows without inspecting every row. A cache stores earlier results for reuse.<\/p>\n<p>The practical question is how much work the actual query performs. Data distribution, indexes, result size, sorting and cache hits all affect the cost. A large metadata table is a reason to measure, not proof that every filter is slow.<\/p>\n<figure class=\"lw-diagram\">\n<img decoding=\"async\" alt=\"Three equality tests: AND uses three joins, compatible OR uses one. The result logic differs; this is not a runtime benchmark.\" height=\"580\" loading=\"lazy\" src=\"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/diagrams\/quality-10392-20261024.png\" width=\"1120\"\/>\n<figcaption>Three equality tests: AND uses three joins, compatible OR uses one. The result logic differs; this is not a runtime benchmark.<\/figcaption>\n<\/figure>\n<h2>What WP_Query Builds from the Array<\/h2>\n<p>For three equality tests on different keys connected by <code>AND<\/code>, WordPress normally uses three metadata aliases. Compatible sibling clauses can share an alias: the same three tests connected by <code>OR<\/code> use one join in the tested WordPress 7.1.3 core. That changes the meaning from \u201call conditions\u201d to \u201cat least one condition\u201d; replacing AND with OR is not a valid performance fix. The SQL below is a simplified AND example with descriptive aliases.<\/p>\n<figure style=\"--cbb-digits:2\" class=\"cbb cbb--wrap cbb--numbers wp-block-code-beautifier-block-code\" data-lang=\"sql\"><figcaption class=\"cbb-head\"><span class=\"cbb-lang\">SQL<\/span><span class=\"cbb-actions\"><button type=\"button\" class=\"cbb-button cbb-button--wrap\" data-cbb-action=\"wrap\" aria-pressed=\"true\" title=\"Wrap long lines\"><svg aria-hidden=\"true\" focusable=\"false\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\"><path d=\"M2 4h12M2 8h9.5a2.5 2.5 0 0 1 0 5H8m0 0 1.5-1.5M8 13l1.5 1.5M2 12h3\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"\/><\/svg><span class=\"cbb-sr\">Wrap long lines<\/span><\/button><button type=\"button\" class=\"cbb-button cbb-button--copy\" data-cbb-action=\"copy\" data-cbb-done=\"Copied\" data-cbb-failed=\"Press Ctrl+C to copy\"><svg aria-hidden=\"true\" focusable=\"false\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\"><rect x=\"5.5\" y=\"5.5\" width=\"8\" height=\"8.5\" rx=\"1.5\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\"\/><path d=\"M10.5 3.5V3A1.5 1.5 0 0 0 9 1.5H4A1.5 1.5 0 0 0 2.5 3v6.5A1.5 1.5 0 0 0 4 11h.5\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\"\/><\/svg><span class=\"cbb-button-label\" aria-live=\"polite\">Copy<\/span><\/button><\/span><\/figcaption><pre class=\"cbb-pre\" tabindex=\"0\"><code class=\"cbb-code language-sql\" style=\"counter-reset:cbb-line 0;\"><span class=\"cbb-line\"><span class=\"cbb-keyword\">SELECT<\/span> wp_posts.ID <span class=\"cbb-keyword\">FROM<\/span> wp_posts\n<\/span><span class=\"cbb-line\">  <span class=\"cbb-keyword\">INNER<\/span> <span class=\"cbb-keyword\">JOIN<\/span> wp_postmeta pm1 <span class=\"cbb-keyword\">ON<\/span> wp_posts.ID = pm1.post_id\n<\/span><span class=\"cbb-line\">  <span class=\"cbb-keyword\">INNER<\/span> <span class=\"cbb-keyword\">JOIN<\/span> wp_postmeta pm2 <span class=\"cbb-keyword\">ON<\/span> wp_posts.ID = pm2.post_id\n<\/span><span class=\"cbb-line\">  <span class=\"cbb-keyword\">INNER<\/span> <span class=\"cbb-keyword\">JOIN<\/span> wp_postmeta pm3 <span class=\"cbb-keyword\">ON<\/span> wp_posts.ID = pm3.post_id\n<\/span><span class=\"cbb-line\"><span class=\"cbb-keyword\">WHERE<\/span> post_type = <span class=\"cbb-string\">'post'<\/span> <span class=\"cbb-keyword\">AND<\/span> post_status = <span class=\"cbb-string\">'publish'<\/span>\n<\/span><span class=\"cbb-line\">  <span class=\"cbb-keyword\">AND<\/span> ( pm1.meta_key = <span class=\"cbb-string\">'_region'<\/span>  <span class=\"cbb-keyword\">AND<\/span> pm1.meta_value = <span class=\"cbb-string\">'north'<\/span> )\n<\/span><span class=\"cbb-line\">  <span class=\"cbb-keyword\">AND<\/span> ( pm2.meta_key = <span class=\"cbb-string\">'_level'<\/span>   <span class=\"cbb-keyword\">AND<\/span> pm2.meta_value = <span class=\"cbb-string\">'b'<\/span> )\n<\/span><span class=\"cbb-line\">  <span class=\"cbb-keyword\">AND<\/span> ( pm3.meta_key = <span class=\"cbb-string\">'_active'<\/span>  <span class=\"cbb-keyword\">AND<\/span> pm3.meta_value = <span class=\"cbb-string\">'1'<\/span> )\n<\/span><span class=\"cbb-line\"><span class=\"cbb-keyword\">GROUP<\/span> <span class=\"cbb-keyword\">BY<\/span> wp_posts.ID\n<\/span><span class=\"cbb-line\"><span class=\"cbb-keyword\">ORDER<\/span> <span class=\"cbb-keyword\">BY<\/span> wp_posts.post_date <span class=\"cbb-keyword\">DESC<\/span><\/span><\/code><\/pre><\/figure>\n<p>The example contains three joins and four table references including <code>wp_posts<\/code>. Grouping by the post ID prevents repeated IDs when multiple matching metadata rows exist. Neither the SQL text nor the clause count proves that 500,000 rows must be read to return 40 posts. A reproducible dataset and execution plan are needed for such a claim.<\/p>\n<h2>Standard Indexes and Possible Extensions<\/h2><p>A primary key uniquely identifies a row. A prefix index stores only the beginning of a text value. The database optimiser chooses the lookup and join order; an execution plan describes those choices.<\/p>\n<p>The standard WordPress schema has a primary key on <code>meta_id<\/code>, an index on <code>post_id<\/code> and a prefix index on <code>meta_key<\/code> with a standard limit of 191 characters. No <code>meta_value<\/code> index is created by core. The LONGTEXT type does permit prefix indexes; a suitable additional index can help selected workloads, but cannot be prescribed without checking values, key lengths, write cost and database version.<\/p>\n<p>The optimiser may start with posts or with a metadata key that narrows the result to relatively few rows. A lookup by <code>post_id<\/code> is one possible plan, not a fixed sequence. The relevant quantities include matching rows per lookup and the fraction surviving each condition. There is no universal minimum of 30 metadata rows per post.<\/p>\n<h2>Operators and Their Possible Costs<\/h2>\n<table style=\"width:100%;border-collapse:collapse\">\n<thead>\n<tr><th style=\"vertical-align:top\">Clause<\/th><th>Possible work<\/th><\/tr>\n<\/thead>\n<tbody>\n<tr><td style=\"white-space:nowrap;vertical-align:top\"><code>'compare' =&gt; '='<\/code><\/td><td>String equality; join reuse depends on the surrounding clauses.<\/td><\/tr>\n<tr><td style=\"white-space:nowrap;vertical-align:top\"><code>'compare' =&gt; 'LIKE'<\/code><\/td><td>WordPress wraps the value in %&#8230;%, searching for it anywhere in the text. A normal lookup based on the beginning of the value is unsuitable, while indexes on other conditions can still help.<\/td><\/tr>\n<tr><td style=\"white-space:nowrap;vertical-align:top\"><code>'type' =&gt; 'NUMERIC'<\/code><\/td><td>Converts the text to a signed integer with CAST(&#8230; AS SIGNED); a raw text-value index does not directly serve that numeric expression. Other indexes can still be used.<\/td><\/tr>\n<tr><td style=\"white-space:nowrap;vertical-align:top\"><code>'compare' =&gt; 'NOT EXISTS'<\/code><\/td><td>Missing-key test via LEFT JOIN and IS NULL; indexed lookups and early termination may help.<\/td><\/tr>\n<tr><td style=\"white-space:nowrap;vertical-align:top\"><code>'relation' =&gt; 'OR'<\/code><\/td><td>At least one condition must match; compatible clauses may share a join.<\/td><\/tr>\n<tr><td style=\"white-space:nowrap;vertical-align:top\"><code>'orderby' =&gt; 'meta_value'<\/code><\/td><td>May require an extra sort; memory use, disk use and temporary tables depend on the plan.<\/td><\/tr>\n<\/tbody>\n<\/table>\n<p><code>Using filesort<\/code> means an additional sorting step rather than index order. Despite the name, sorting can remain in memory. Disk use depends on the result, settings and database implementation. <code>Using temporary<\/code> means that a temporary table is used during processing. Neither label alone predicts a timeout; elapsed time and the actual plan determine whether optimisation is needed.<\/p>\n<p>A LEFT JOIN keeps the post row even when no metadata row matches. IS NULL can then select the posts without that match. A missing key can be found with <code>LEFT JOIN<\/code> and <code>IS NULL<\/code>. An index can accelerate the search for a matching row, and an optimiser can stop searching after a match rules out that candidate. The claim that indexes cannot help prove absence is therefore wrong. The relevant WordPress operator and generated SQL must still be inspected.<\/p>\n\n\n<!-- lw-flux-illu 10392-a3 -->\n<figure class=\"lw-illu\"><img src=\"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/illustrationen\/flux-10392-a3.webp\" width=\"1120\" height=\"630\" loading=\"lazy\" decoding=\"async\" alt=\"A magnet pulls the needle straight out of the haystack, while a second heap is searched stalk by stalk. Likewise, a search using an index is fast, while a search anywhere in the text must go through everything.\"><figcaption>A magnet pulls the needle straight out of the haystack, while a second heap is searched stalk by stalk. Likewise, a search using an index is fast, while a search anywhere in the text must go through everything.<\/figcaption><\/figure>\n\n\n<h2>Choosing Metadata, Taxonomy or a Dedicated Table<\/h2>\n<p>Taxonomies suit shared classifications such as regions or product groups. The standard relationship table has a composite primary key on <code>(object_id, term_taxonomy_id)<\/code> and a separate index on <code>term_taxonomy_id<\/code>; it also contains <code>term_order<\/code>. A taxonomy query can still require multiple joins or subqueries. A faster result is not guaranteed by the table name.<\/p>\n<p>Metadata remains reasonable for many small or infrequently used filters. Frequently queried numeric ranges and ordered values may benefit from a dedicated table with typed columns and suitable indexes. The decision should follow the data model and measured workload. Reading metadata after a post lookup may use an object cache or a batch query; it is not universally one SQL lookup per field.<\/p>\n<h2>Measuring Instead of Guessing<\/h2>\n<p>After a normal <code>WP_Query<\/code> call, <code>$query-&gt;request<\/code> exposes the constructed SQL; the call may already have executed that SQL. The <code>posts_request<\/code> filter provides a point before the main post query is sent. A plain <code>EXPLAIN<\/code> of the complete SELECT shows the estimated plan, not a measured runtime. Database-specific execution-analysis commands actually run the query and belong on a suitable test copy.<\/p>\n<figure style=\"--cbb-digits:1\" class=\"cbb cbb--wrap cbb--numbers wp-block-code-beautifier-block-code\" data-lang=\"plain\"><figcaption class=\"cbb-head\"><span class=\"cbb-lang\">Text<\/span><span class=\"cbb-actions\"><button type=\"button\" class=\"cbb-button cbb-button--wrap\" data-cbb-action=\"wrap\" aria-pressed=\"true\" title=\"Wrap long lines\"><svg aria-hidden=\"true\" focusable=\"false\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\"><path d=\"M2 4h12M2 8h9.5a2.5 2.5 0 0 1 0 5H8m0 0 1.5-1.5M8 13l1.5 1.5M2 12h3\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"\/><\/svg><span class=\"cbb-sr\">Wrap long lines<\/span><\/button><button type=\"button\" class=\"cbb-button cbb-button--copy\" data-cbb-action=\"copy\" data-cbb-done=\"Copied\" data-cbb-failed=\"Press Ctrl+C to copy\"><svg aria-hidden=\"true\" focusable=\"false\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\"><rect x=\"5.5\" y=\"5.5\" width=\"8\" height=\"8.5\" rx=\"1.5\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\"\/><path d=\"M10.5 3.5V3A1.5 1.5 0 0 0 9 1.5H4A1.5 1.5 0 0 0 2.5 3v6.5A1.5 1.5 0 0 0 4 11h.5\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\"\/><\/svg><span class=\"cbb-button-label\" aria-live=\"polite\">Copy<\/span><\/button><\/span><\/figcaption><pre class=\"cbb-pre\" tabindex=\"0\"><code class=\"cbb-code language-plain\" style=\"counter-reset:cbb-line 0;\"><span class=\"cbb-line\">EXPLAIN: estimated plan\n<\/span><span class=\"cbb-line\">key       chosen index\n<\/span><span class=\"cbb-line\">rows      estimated examined rows\n<\/span><span class=\"cbb-line\">filtered  estimated surviving percentage (MySQL)\n<\/span><span class=\"cbb-line\">Extra     additional operations<\/span><\/code><\/pre><\/figure>\n<p><code>rows<\/code> is an estimate, and MySQL\u2019s <code>filtered<\/code> estimates the percentage surviving a condition. Join order and surviving intermediate rows matter. Multiplying every rows value, or adding a fixed cost per join, does not establish the actual total read count. A useful comparison records database version, schema, indexes, representative data, query parameters and timing, with cached results available (warm cache) and without them (cold cache).<\/p>\n<div class=\"lw-faq\">\n<h2>Questions and answers<\/h2>\n<h3>Does a leading underscore in the key, as in _region, make the query faster?<\/h3>\n<p>No performance property follows from the underscore. WordPress normally treats an underscore-prefixed metadata key as protected and hides it in the classic Custom Fields interface; filters can change that behaviour. This is an application convention, not a database permission or special index feature.<\/p>\n<h3>Does a persistent object cache help against an expensive meta_query?<\/h3>\n<p>A cache hit can genuinely avoid the main SQL query. WordPress added WP_Query result caching in 6.1, with persistent object caching keeping results available across separate web requests. Whether the query can be cached, the <code>cache_results<\/code> setting, removal of cached entries and marking outdated results unusable still matter. When WordPress marks post caches outdated, it changes a shared version marker. This can make cached query results unusable beyond the edited post. Direct SQL updates can instead bypass that step and leave outdated results in the cache.<\/p>\n<p>Three independent filters with five selectable values each have 125 combinations when all three are set. Only requested combinations need to be computed, and popular combinations may achieve a high hit rate. Frequent updates can reduce reuse without making caching automatically useless.<\/p>\n<p>Both cold and warm performance matter. A query plan helps explain a cache miss; observed traffic and hit rates show how often that cost occurs. Page caching also depends on URL and query-string handling, invalidation and configuration. Neither object caching nor a schema change should be selected from an unmeasured worst-case illustration.<\/p>\n<\/div>\n<div class=\"lw-quellen\">\n<h2>Sources<\/h2>\n<ul><li><a href=\"https:\/\/developer.wordpress.org\/reference\/classes\/wp_query\/\" rel=\"noopener noreferrer\" target=\"_blank\">WordPress: WP_Query<\/a><\/li><li><a href=\"https:\/\/developer.wordpress.org\/reference\/classes\/wp_meta_query\/find_compatible_table_alias\/\" rel=\"noopener noreferrer\" target=\"_blank\">WordPress: find_compatible_table_alias<\/a><\/li><li><a href=\"https:\/\/developer.wordpress.org\/reference\/classes\/wp_meta_query\/get_sql_for_clause\/\" rel=\"noopener noreferrer\" target=\"_blank\">WordPress: get_sql_for_clause<\/a><\/li><li><a href=\"https:\/\/dev.mysql.com\/doc\/refman\/8.4\/en\/explain-output.html\" rel=\"noopener noreferrer\" target=\"_blank\">MySQL: EXPLAIN Output Format<\/a><\/li><li><a href=\"https:\/\/dev.mysql.com\/doc\/refman\/8.4\/en\/order-by-optimization.html\" rel=\"noopener noreferrer\" target=\"_blank\">MySQL: ORDER BY Optimization<\/a><\/li><li><a href=\"https:\/\/dev.mysql.com\/doc\/refman\/8.4\/en\/column-indexes.html\" rel=\"noopener noreferrer\" target=\"_blank\">MySQL: Column Indexes<\/a><\/li><li><a href=\"https:\/\/make.wordpress.org\/core\/2022\/10\/07\/improvements-to-wp_query-performance-in-6-1\/\" rel=\"noopener noreferrer\" target=\"_blank\">WordPress: WP_Query Caching in 6.1<\/a><\/li><li><a href=\"https:\/\/developer.wordpress.org\/reference\/functions\/clean_post_cache\/\" rel=\"noopener noreferrer\" target=\"_blank\">WordPress: clean_post_cache<\/a><\/li><li><a href=\"https:\/\/developer.wordpress.org\/reference\/functions\/is_protected_meta\/\" rel=\"noopener noreferrer\" target=\"_blank\">WordPress: is_protected_meta<\/a><\/li><\/ul>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>WordPress metadata filters can require several joins, but join count alone does not predict runtime. Examples explain indexes, operators, query plans and comparisons with and without cached results.<\/p>\n","protected":false},"author":1,"featured_media":31647,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[9],"tags":[91104,91315],"class_list":["post-10392","post","type-post","status-publish","format-standard","hentry","category-wordpress-plugins-tricks","tag-mysql","tag-web-performance"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v28.1 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Investigating Slow WordPress Filters: How meta_query Queries the Database | Lukas Wojcik<\/title>\n<meta name=\"description\" content=\"How WP_Query turns meta_query into joins, why meta_value cannot be indexed usefully, and which filters belong in a taxonomy instead.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.lukaswojcik.com\/blog\/en\/wordpress-plugins-tricks\/investigating-slow-wordpress-filters-how-meta_query-queries-the-database\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Investigating Slow WordPress Filters: How meta_query Queries the Database | Lukas Wojcik\" \/>\n<meta property=\"og:description\" content=\"How WP_Query turns meta_query into joins, why meta_value cannot be indexed usefully, and which filters belong in a taxonomy instead.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.lukaswojcik.com\/blog\/en\/wordpress-plugins-tricks\/investigating-slow-wordpress-filters-how-meta_query-queries-the-database\/\" \/>\n<meta property=\"og:site_name\" content=\"Lukas Wojcik - Blog\" \/>\n<meta property=\"article:published_time\" content=\"2026-09-12T05:20:00+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2026-10-26T12:49:06+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/11\/klarheit-20261111-10392.png\" \/>\n\t<meta property=\"og:image:width\" content=\"1200\" \/>\n\t<meta property=\"og:image:height\" content=\"630\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"Lukas Wojcik\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Lukas Wojcik\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"7 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/wordpress-plugins-tricks\\\/investigating-slow-wordpress-filters-how-meta_query-queries-the-database\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/wordpress-plugins-tricks\\\/investigating-slow-wordpress-filters-how-meta_query-queries-the-database\\\/\"},\"author\":{\"name\":\"Lukas Wojcik\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/#\\\/schema\\\/person\\\/895f7604f9b6b71aad9bba33af28d0f9\"},\"headline\":\"Investigating Slow WordPress Filters: How meta_query Queries the Database\",\"datePublished\":\"2026-09-12T05:20:00+00:00\",\"dateModified\":\"2026-10-26T12:49:06+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/wordpress-plugins-tricks\\\/investigating-slow-wordpress-filters-how-meta_query-queries-the-database\\\/\"},\"wordCount\":1260,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/#\\\/schema\\\/person\\\/895f7604f9b6b71aad9bba33af28d0f9\"},\"image\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/wordpress-plugins-tricks\\\/investigating-slow-wordpress-filters-how-meta_query-queries-the-database\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/11\\\/klarheit-20261111-10392.png\",\"keywords\":[\"MySQL\",\"Web Performance\"],\"articleSection\":[\"WordPress Plugins &amp; Tricks\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/wordpress-plugins-tricks\\\/investigating-slow-wordpress-filters-how-meta_query-queries-the-database\\\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/wordpress-plugins-tricks\\\/investigating-slow-wordpress-filters-how-meta_query-queries-the-database\\\/\",\"url\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/wordpress-plugins-tricks\\\/investigating-slow-wordpress-filters-how-meta_query-queries-the-database\\\/\",\"name\":\"Investigating Slow WordPress Filters: How meta_query Queries the Database | Lukas Wojcik\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/wordpress-plugins-tricks\\\/investigating-slow-wordpress-filters-how-meta_query-queries-the-database\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/wordpress-plugins-tricks\\\/investigating-slow-wordpress-filters-how-meta_query-queries-the-database\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/11\\\/klarheit-20261111-10392.png\",\"datePublished\":\"2026-09-12T05:20:00+00:00\",\"dateModified\":\"2026-10-26T12:49:06+00:00\",\"description\":\"How WP_Query turns meta_query into joins, why meta_value cannot be indexed usefully, and which filters belong in a taxonomy instead.\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/wordpress-plugins-tricks\\\/investigating-slow-wordpress-filters-how-meta_query-queries-the-database\\\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/wordpress-plugins-tricks\\\/investigating-slow-wordpress-filters-how-meta_query-queries-the-database\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/wordpress-plugins-tricks\\\/investigating-slow-wordpress-filters-how-meta_query-queries-the-database\\\/#primaryimage\",\"url\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/11\\\/klarheit-20261111-10392.png\",\"contentUrl\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/11\\\/klarheit-20261111-10392.png\",\"width\":1200,\"height\":630,\"caption\":\"Investigating Slow WordPress Filters: How meta_query Queries the Database\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/wordpress-plugins-tricks\\\/investigating-slow-wordpress-filters-how-meta_query-queries-the-database\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Investigating Slow WordPress Filters: How meta_query Queries the Database\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/#website\",\"url\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/\",\"name\":\"Lukas Wojcik - Blog\",\"description\":\"\",\"publisher\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/#\\\/schema\\\/person\\\/895f7604f9b6b71aad9bba33af28d0f9\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":[\"Person\",\"Organization\"],\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/#\\\/schema\\\/person\\\/895f7604f9b6b71aad9bba33af28d0f9\",\"name\":\"Lukas Wojcik\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/07\\\/lw-x2.jpg\",\"url\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/07\\\/lw-x2.jpg\",\"contentUrl\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/07\\\/lw-x2.jpg\",\"width\":424,\"height\":636,\"caption\":\"Lukas Wojcik\"},\"logo\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/07\\\/lw-x2.jpg\"},\"sameAs\":[\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\"]}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Investigating Slow WordPress Filters: How meta_query Queries the Database | Lukas Wojcik","description":"How WP_Query turns meta_query into joins, why meta_value cannot be indexed usefully, and which filters belong in a taxonomy instead.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.lukaswojcik.com\/blog\/en\/wordpress-plugins-tricks\/investigating-slow-wordpress-filters-how-meta_query-queries-the-database\/","og_locale":"en_US","og_type":"article","og_title":"Investigating Slow WordPress Filters: How meta_query Queries the Database | Lukas Wojcik","og_description":"How WP_Query turns meta_query into joins, why meta_value cannot be indexed usefully, and which filters belong in a taxonomy instead.","og_url":"https:\/\/www.lukaswojcik.com\/blog\/en\/wordpress-plugins-tricks\/investigating-slow-wordpress-filters-how-meta_query-queries-the-database\/","og_site_name":"Lukas Wojcik - Blog","article_published_time":"2026-09-12T05:20:00+00:00","article_modified_time":"2026-10-26T12:49:06+00:00","og_image":[{"width":1200,"height":630,"url":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/11\/klarheit-20261111-10392.png","type":"image\/png"}],"author":"Lukas Wojcik","twitter_card":"summary_large_image","twitter_misc":{"Written by":"Lukas Wojcik","Est. reading time":"7 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.lukaswojcik.com\/blog\/en\/wordpress-plugins-tricks\/investigating-slow-wordpress-filters-how-meta_query-queries-the-database\/#article","isPartOf":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/en\/wordpress-plugins-tricks\/investigating-slow-wordpress-filters-how-meta_query-queries-the-database\/"},"author":{"name":"Lukas Wojcik","@id":"https:\/\/www.lukaswojcik.com\/blog\/#\/schema\/person\/895f7604f9b6b71aad9bba33af28d0f9"},"headline":"Investigating Slow WordPress Filters: How meta_query Queries the Database","datePublished":"2026-09-12T05:20:00+00:00","dateModified":"2026-10-26T12:49:06+00:00","mainEntityOfPage":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/en\/wordpress-plugins-tricks\/investigating-slow-wordpress-filters-how-meta_query-queries-the-database\/"},"wordCount":1260,"commentCount":0,"publisher":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/#\/schema\/person\/895f7604f9b6b71aad9bba33af28d0f9"},"image":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/en\/wordpress-plugins-tricks\/investigating-slow-wordpress-filters-how-meta_query-queries-the-database\/#primaryimage"},"thumbnailUrl":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/11\/klarheit-20261111-10392.png","keywords":["MySQL","Web Performance"],"articleSection":["WordPress Plugins &amp; Tricks"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/www.lukaswojcik.com\/blog\/en\/wordpress-plugins-tricks\/investigating-slow-wordpress-filters-how-meta_query-queries-the-database\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/www.lukaswojcik.com\/blog\/en\/wordpress-plugins-tricks\/investigating-slow-wordpress-filters-how-meta_query-queries-the-database\/","url":"https:\/\/www.lukaswojcik.com\/blog\/en\/wordpress-plugins-tricks\/investigating-slow-wordpress-filters-how-meta_query-queries-the-database\/","name":"Investigating Slow WordPress Filters: How meta_query Queries the Database | Lukas Wojcik","isPartOf":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/en\/wordpress-plugins-tricks\/investigating-slow-wordpress-filters-how-meta_query-queries-the-database\/#primaryimage"},"image":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/en\/wordpress-plugins-tricks\/investigating-slow-wordpress-filters-how-meta_query-queries-the-database\/#primaryimage"},"thumbnailUrl":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/11\/klarheit-20261111-10392.png","datePublished":"2026-09-12T05:20:00+00:00","dateModified":"2026-10-26T12:49:06+00:00","description":"How WP_Query turns meta_query into joins, why meta_value cannot be indexed usefully, and which filters belong in a taxonomy instead.","breadcrumb":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/en\/wordpress-plugins-tricks\/investigating-slow-wordpress-filters-how-meta_query-queries-the-database\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.lukaswojcik.com\/blog\/en\/wordpress-plugins-tricks\/investigating-slow-wordpress-filters-how-meta_query-queries-the-database\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.lukaswojcik.com\/blog\/en\/wordpress-plugins-tricks\/investigating-slow-wordpress-filters-how-meta_query-queries-the-database\/#primaryimage","url":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/11\/klarheit-20261111-10392.png","contentUrl":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/11\/klarheit-20261111-10392.png","width":1200,"height":630,"caption":"Investigating Slow WordPress Filters: How meta_query Queries the Database"},{"@type":"BreadcrumbList","@id":"https:\/\/www.lukaswojcik.com\/blog\/en\/wordpress-plugins-tricks\/investigating-slow-wordpress-filters-how-meta_query-queries-the-database\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.lukaswojcik.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Investigating Slow WordPress Filters: How meta_query Queries the Database"}]},{"@type":"WebSite","@id":"https:\/\/www.lukaswojcik.com\/blog\/#website","url":"https:\/\/www.lukaswojcik.com\/blog\/","name":"Lukas Wojcik - Blog","description":"","publisher":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/#\/schema\/person\/895f7604f9b6b71aad9bba33af28d0f9"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.lukaswojcik.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":["Person","Organization"],"@id":"https:\/\/www.lukaswojcik.com\/blog\/#\/schema\/person\/895f7604f9b6b71aad9bba33af28d0f9","name":"Lukas Wojcik","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/07\/lw-x2.jpg","url":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/07\/lw-x2.jpg","contentUrl":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/07\/lw-x2.jpg","width":424,"height":636,"caption":"Lukas Wojcik"},"logo":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/07\/lw-x2.jpg"},"sameAs":["https:\/\/www.lukaswojcik.com\/blog"]}]}},"_links":{"self":[{"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/posts\/10392","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/comments?post=10392"}],"version-history":[{"count":7,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/posts\/10392\/revisions"}],"predecessor-version":[{"id":27536,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/posts\/10392\/revisions\/27536"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/media\/31647"}],"wp:attachment":[{"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/media?parent=10392"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/categories?post=10392"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/tags?post=10392"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}