#19729 closed enhancement (fixed)
WP_Meta_Query is inefficient when only specifying keys in "OR" query
| Reported by: |
|
Owned by: |
|
|---|---|---|---|
| Priority: | normal | Milestone: | 3.5 |
| Component: | Query | Version: | 3.3 |
| Severity: | normal | Keywords: | has-patch 3.5-early commit needs-unit-tests |
| Cc: | adam@…, aaroncampbell, scribu, batmoo@…, lol@… |
Description
Suppose I have a WP_Query like so:
$query = new WP_Query( array( 'meta_query' => array( array( 'key' => 'foo' ), array( 'key' => 'bar', 'value' => 'val2' ), array( 'key' => 'baz' ), array( 'key' => 'froo' ) 'relation' => 'OR' ) ) );
Currently, WP_Meta_Query will JOIN 4 copies of postmeta and use one JOIN per key - however, when using a relation of OR, it's not necessary to use more than one meta JOIN for the key-only queries.
The attached patch will pull out all the "key only" queries and use a single JOIN for them, leaving the remaining quires in-tact as they were before.
This can only be done with an OR query, of course.
Below is the SQL query before the patch:
SELECT SQL_CALC_FOUND_ROWS wp_posts.* FROM wp_posts INNER JOIN wp_postmeta ON (wp_posts.ID = wp_postmeta.post_id) INNER JOIN wp_postmeta AS mt1 ON (wp_posts.ID = mt1.post_id) INNER JOIN wp_postmeta AS mt2 ON (wp_posts.ID = mt2.post_id) INNER JOIN wp_postmeta AS mt3 ON (wp_posts.ID = mt3.post_id) WHERE 1=1 AND wp_posts.post_type = 'post' AND (wp_posts.post_status = 'publish' OR wp_posts.post_status = 'private') AND (wp_postmeta.meta_key = 'foo' OR (mt1.meta_key = 'bar' AND CAST(mt1.meta_value AS CHAR) = 'val2') OR mt2.meta_key = 'baz' OR mt3.meta_key = 'froo' ) GROUP BY wp_posts.ID ORDER BY wp_posts.post_date DESC LIMIT 0, 10
The same after the patch:
SELECT SQL_CALC_FOUND_ROWS wp_posts.* FROM wp_posts INNER JOIN wp_postmeta ON wp_posts.ID = wp_postmeta.post_id INNER JOIN wp_postmeta AS mt1 ON (wp_posts.ID = mt1.post_id) WHERE 1=1 AND wp_posts.post_type = 'post' AND (wp_posts.post_status = 'publish' OR wp_posts.post_status = 'private') AND (wp_postmeta.meta_key = 'foo' OR wp_postmeta.meta_key = 'baz' OR wp_postmeta.meta_key = 'froo' OR (mt1.meta_key = 'bar' AND CAST(mt1.meta_value AS CHAR) = 'val2') ) GROUP BY wp_posts.ID ORDER BY wp_posts.post_date DESC LIMIT 0, 10
I ran this on a large site (>100k posts), so not sure if the performance gains will be so drastic for smaller sites. Before the patch, with 3 JOINS took around 43seconds. After the patch with 1 Join, 5.2ms. Not sure why the hug difference, presumably multiple JOINS was killing it somehow.
Attachments (4)
Change History (28)
comment:2
AdamBackstrom — 17 months ago
- Cc adam@… added
comment:3
Viper007Bond — 17 months ago
Quite the speed improvement!
And here's an easier to read (IMO) of the example:
$query = new WP_Query( array( 'meta_query' => array( array( 'key' => 'foo' ), array( 'key' => 'bar', 'value' => 'val2' ), array( 'key' => 'baz' ), array( 'key' => 'froo' ), 'relation' => 'OR', ), ) );
comment:5
aaroncampbell — 13 months ago
- Cc aaroncampbell added
- Keywords 3.5-early added
- Milestone changed from 3.4 to Future Release
Agreed. Let's not forget.
comment:10
markjaquith — 10 months ago
- Milestone changed from Future Release to 3.5
Resurrecting this for 3.5.
comment:11
joehoyle — 10 months ago
Just wanted to bump this, has-patch, presuming needs some peer review?
comment:12
ryan — 9 months ago
We really need some unit tests here. Then we could clear these patches faster.
comment:13
follow-up:
↓ 14
joehoyle — 9 months ago
I don't mind writing some basic tests - haven't done it for WordPress Core before though, could anyone point me in the right direction to get set up
comment:14
in reply to:
↑ 13
SergeyBiryukov — 9 months ago
comment:15
sc0ttkclark — 9 months ago
- Cc lol@… added
comment:16
nacin — 8 months ago
If the relation is not 'OR' then we should be able to bypass that first foreach all together, instead assigning $queries = $this->queries.
comment:17
nacin — 8 months ago
- Keywords needs-refresh commit needs-unit-tests added
I really want this, and it is a good commit candidate. But, needs a refresh (see also comment 16) and could use a unit test or two.
SergeyBiryukov — 8 months ago
comment:18
SergeyBiryukov — 8 months ago
- Keywords needs-refresh removed
comment:19
ryan — 8 months ago
There's a start on post meta query tests.
comment:20
ryan — 8 months ago
I've been going through old meta query tickets and creating unit test coverage for each fixed bug.
http://unit-tests.trac.wordpress.org/browser/trunk/tests/post/query.php
Contributions welcome. Soon I think I'll be comfortable landing 19729.diff.
comment:21
ryan — 8 months ago
- Owner set to ryan
- Resolution set to fixed
- Status changed from new to closed
In [22103]:
comment:22
joehoyle — 5 months ago
Added some unit tests for WP_Meta_Query and specially one to address this ticket (and also http://unit-tests.trac.wordpress.org/ticket/122)
First unit test so let me know if anything needs changing
comment:23
scribu — 5 months ago
The only thing I had to change was
@ticket http://core.trac.wordpress.org/ticket/19729
with
@ticket 19729
comment:24
SergeyBiryukov — 5 weeks ago
Follow-up: #24093

Nice.