[wp-trac] [WordPress Trac] #61502: Improve `wp_count_posts()` query performance for users without `read_private_posts` capabilities
WordPress Trac
noreply at wordpress.org
Tue Jun 25 17:11:16 UTC 2024
#61502: Improve `wp_count_posts()` query performance for users without
`read_private_posts` capabilities
-------------------------------+-----------------------------
Reporter: rcorrales | Owner: (none)
Type: enhancement | Status: new
Priority: normal | Milestone: Awaiting Review
Component: Posts, Post Types | Version:
Severity: normal | Keywords:
Focuses: performance |
-------------------------------+-----------------------------
This ticket separates the work needed for the filter creation proposed in
#61097 and focuses here on the performance optimization required.
== Context:
Queries generated by `the wp_count_posts()` function for users without the
`read_private_posts` capability incorporate the following conditions:
{{{#!php
<?php
if ( ! current_user_can( $post_type_object->cap->read_private_posts ) ) {
$query .= $wpdb->prepare(
" AND (post_status != 'private' OR ( post_author = %d AND
post_status = 'private' ))",
get_current_user_id()
);
}
}}}
This doesn't efficiently use indexes and makes the query extremely slow if
there are millions of records in the `wp_posts` table.
== Proposal:
Split the query into two simpler ones and join the results using `UNION
ALL` so it makes better use of the existing indexes:
{{{#!php
SELECT post_status, COUNT(*) AS num_posts
FROM (
SELECT post_status
FROM wp_posts
WHERE post_type = %s AND post_status != 'private'
UNION ALL
SELECT post_status
FROM wp_posts
WHERE post_type = %s AND post_status = 'private' AND post_author = %d
) AS filtered_posts
GROUP BY post_status;
}}}
I tested this on a table with +14M records, and query time went down from
8 minutes to 11 seconds, generating the same results.
== To discuss:
I haven't seen any examples of `UNION ALL` operators in core, even though
MySQL has supported them for a long time (since the early 2000s). I'm
unsure if it's by design or if there simply hasn't been a need for that.
If using `UNION ALL` is not permitted, then splitting the query into two
`get_results()` calls and merging the results back in the app layer might
also be an alternative, although a less performant one because of the
network and code overhead. But it's possibly still better than executing
the original query.
--
Ticket URL: <https://core.trac.wordpress.org/ticket/61502>
WordPress Trac <https://core.trac.wordpress.org/>
WordPress publishing platform
More information about the wp-trac
mailing list