Hi, this query recognized by Query Monitor as the “slow query” belongs to core WP/CP and ideed it’s very unoptimal. It’s runing on wp-admin/post.php to fulfill custom fields keys.
SELECT DISTINCT meta_key
FROM et_postmeta
WHERE meta_key NOT BETWEEN '_'
AND '_z'
HAVING meta_key NOT LIKE '\\_%'
ORDER BY meta_key
LIMIT 30
query timing on localhost:
0.4180 s, (~500 000 rows in et_postmeta)
after cleaning the table:
0.0910 s. (~77 000 rows, still shows up as a slow query)
the code is located in /wp-admin/includes/template.php
Although it can be replaced by filter, but then the keys would be enforced and not dynamical so it’s not a good idea.
I have written the optimized version of this query, which is based on last posts with the same post_type, which is even more adequate in my opinion. I send it here as the purpose for the core ClassicPress development:
// @jujist replace slow query to one running extra-fast ~0.00s.
// param int $limit now is the number of last posts to check instead of limiting the keys
$keys = $wpdb->get_col($wpdb->prepare("
SELECT meta_key
FROM $wpdb->posts as p
LEFT JOIN $wpdb->postmeta as pm ON (p.ID=pm.post_id)
WHERE post_type='$post->post_type'
AND SUBSTR(meta_key,1,1)!='_'
ORDER BY ID DESC LIMIT 0, %d
", $limit));
$keys = array_unique($keys);
query timing on localhost: 0.0006 s in both cases (before/after cleanup)
QUERY TIME REDUCTION
original table 99,86% REDUCTION
after cleaning the table 99,34% REDUCTION
This query is really a bottleneck for post.php load time.
the begining time was 0.4535 s. for all SQL queries and 0.52 s. for page load
using the optimized query it’s 0.0380 s. for all SQL queries and 0.10 s. for page load
So with this single change of one query I have obtained 81% reduction of loading post.php
Let’s disclose the difference. The $limit parameter originally limits the number of keys based on alphabetical list, but in my query it will limit the number of last posts to check by the same post_type. In my opinion is okay to do it globally as it shows the full list of custom fields keys belongs to the recent X posts.
In real case before the change we could see a lot of junk keys stored many years ago,
and if there will be more than 30 (default limit value) different keys stored in the postmeta table, then we will not see the ones we really need
after the change we see the actual keys filled for X recent posts by the same post_type,
simple and bright ![]()


