WordPress: Heavy slow query - php

In a client website that should display upcoming territory events I got a plugin (called EventsManager), that has been modified to fit our needs.
That plugin, by the way, got an huge query searching for the current events by two parameters, date_start and date_end.
This query affect only one page, and only one function.
$args_filter = $tax_query = array();
$paged = '';
$args_basic = $this->ovaem_query_base($paged, $orderby, $order, $show_past);
switch ($filter) {
[...]
case 'upcomming_showing':
$args_filter = array(
'posts_per_page' => $count,
'meta_query' => array(
array(
'relation' => 'OR',
array(
'key' => self::$prefix . '_date_start_time',
'value' => current_time('timestamp'),
'compare' => '>',
),
array(
'relation' => 'AND',
array(
'key' => self::$prefix . '_date_start_time',
'value' => current_time('timestamp'),
'compare' => '<',
),
array(
'key' => self::$prefix . '_date_end_time',
'value' => current_time('timestamp'),
'compare' => '>=',
),
),
),
),
);
break;
[...]
$args = array_merge_recursive($args_basic, $args_filter, $tax_query);
$eventlist = new WP_Query($args);
return $eventlist;
The resultant query, analized with QueryMonitor is:
SELECT SQL_CALC_FOUND_ROWS wpzt_posts.ID
FROM wpzt_posts
INNER JOIN wpzt_postmeta
ON ( wpzt_posts.ID = wpzt_postmeta.post_id )
INNER JOIN wpzt_postmeta AS mt1
ON ( wpzt_posts.ID = mt1.post_id )
INNER JOIN wpzt_postmeta AS mt2
ON ( wpzt_posts.ID = mt2.post_id )
INNER JOIN wpzt_postmeta AS mt3
ON ( wpzt_posts.ID = mt3.post_id )
WHERE 1=1
AND ( wpzt_postmeta.meta_key = 'ovaem_date_start_time'
AND ( ( ( mt1.meta_key = 'ovaem_date_start_time'
AND CAST(mt1.meta_value AS SIGNED) BETWEEN '1643155199'
AND '1643241601' )
OR ( ( mt2.meta_key = 'ovaem_date_start_time'
AND mt2.meta_value < '1643155200' )
AND ( mt3.meta_key = 'ovaem_date_end_time'
AND mt3.meta_value >= '1643155200' ) ) ) ) )
AND wpzt_posts.post_type = 'event'
AND ((wpzt_posts.post_status = 'publish'))
GROUP BY wpzt_posts.ID
ORDER BY wpzt_postmeta.meta_value+0 ASC
LIMIT 0, 9
Using MySQL 5.7 and memcached, this query got 16,69 seconds to be executed.
Is there a way to optimize that?
Thanks

How the query comes out of set of code is unclear. But a direct query (inserting your parameters as applicable) could be simplified to
SELECT SQL_CALC_FOUND_ROWS
p.ID
FROM
wpzt_posts p
JOIN wpzt_postmeta mtStart
ON p.ID = mtStart.post_id
AND mtStart.meta_key = 'ovaem_date_start_time'
LEFT JOIN wpzt_postmeta mtEnd
ON p.ID = mtEnd.post_id
AND mtEnd.meta_key = 'ovaem_date_end_time'
AND mtEnd.meta_value >= '1643155200'
WHERE
p.post_type = 'event'
AND p.post_status = 'publish'
AND (
mtStart.meta_value BETWEEN '1643155199' AND '1643241601'
OR ( mtStart.meta_value < '1643155200'
AND mtEnd.meta_value > '1643155200'
)
GROUP BY
p.ID
ORDER BY
mtStart.meta_value + 0 ASC
LIMIT
0, 9
So, not knowing specifically about WordPress and how you have it configured, Does your data allow for multiple start and end time entries for a SINGLE POST ID? If so, that will result in a Cartesian Result thus over multiplying the instances.
Are you trying to count how many UNIQUE Post IDs are found, or total records found for all the meta-data entries associated with said posts.
For indexes, I would suggest having the following
Table Index on
wpzt_posts ( post_type, post_status, id )
wpzt_postmeta ( post_id, meta_key, meta_value )
I removed the CAST(meta_value AS SIGNED) since the records associated with date/time context will always be numeric, its not like you would ever encounter a time of 'this is bad data' and thus influence a bad numeric conversion. As such, the function call makes it non-sargeable and thus will not utilize an index. But if all instances of a start/end time are all digits, you wont have that confusion -- unless the physical lengths of those numeric representations actually are longer or shorter, but even in that case, a smaller number would probably indicate a date sooooo far back, it would not even be in the range being considered and thus no impact.

Related

WP_Query: second parameter in meta_query being ignored

The second meta_query parameter is being ignored in this query.
$query = new WP_Query(array(
'post_type' => 'events',
'meta_query' => array(
'relation' => 'OR',
array(
'key' => 'event_date',
'value' => date('Ymd'),
'compare' => '>=',
),
array(
'key' => 'end_date',
'value' => date('Ymd'),
'compare' => '>=',
)
)
));
There is no mention of end_date in the resulting SQL
SELECT SQL_CALC_FOUND_ROWS inx9uju_posts.ID
FROM inx9uju_posts
INNER JOIN inx9uju_postmeta ON ( inx9uju_posts.ID = inx9uju_postmeta.post_id )
INNER JOIN inx9uju_postmeta AS mt1 ON ( inx9uju_posts.ID = mt1.post_id )
WHERE 1=1
AND ( inx9uju_postmeta.meta_key = 'event_date' AND ( mt1.meta_key = 'event_date' AND CAST(mt1.meta_value AS SIGNED) > '20180703' ) )
AND inx9uju_posts.post_type = 'events'
AND (inx9uju_posts.post_status = 'publish'
OR inx9uju_posts.post_status = 'acf-disabled'
OR inx9uju_posts.post_status = 'private')
GROUP BY inx9uju_posts.ID
ORDER BY inx9uju_postmeta.meta_value+0 ASC LIMIT 0, 30
I've spent some time checking syntax and referring to the codex but I can't see what is wrong with my code. I can't see any reason why it would be ignoring the end_date part of the meta_query parameters.
UPDATE
In this particular case I was overriding the meta_query part of the query in a separate function which was hooking into pre_get_posts. Once I updated that function, the issue was resolved.
If this is stored as an AFC, you must use the ACF syntax of Ymd (20180703).
You must also realise that WordPress can detect the BETWEEN meta_compare to detect two dates as such.

Use wp_query with multiple post types but apply meta_query on only one post_type

I would like to wp_query all my given post_types and apply a filter on one of the four post_types without excluding posts of the other post_types.
For example, I would like to find all posts that have P110612 in it. This code is the WooCommerce sku.
So in order for me to query on the _sku key, I apply this as a meta_query.
But, in order for me to not exclude posts without the meta key _sku, I can make an OR relation, like so:
'meta_query' => [
'relation' => 'OR',
[
'key' => '_sku',
'value' => 'P110612',
'compare' => 'LIKE',
],
[
'key' => '_sku',
'value' => 'completely',
'compare' => 'NOT EXISTS',
],
];
What this should do is show all product posts with sku P110612 and all non-product posts where P110612 is somewhere in the content. But, the result is nothing.
Below you'll find the actual query that is being executed by WordPress WP_Query.
I hope you guys can help me out!
SELECT prefix_posts.*
FROM prefix_posts
LEFT JOIN prefix_postmeta ON ( prefix_posts.ID = prefix_postmeta.post_id )
LEFT JOIN prefix_postmeta AS mt1 ON (prefix_posts.ID = mt1.post_id AND mt1.meta_key = '_sku' )
WHERE 1=1
AND prefix_posts.ID NOT IN (2,10,11,12)
AND (
(
(prefix_posts.post_title LIKE '%P110612%')
OR (prefix_posts.post_excerpt LIKE '%P110612%')
OR (prefix_posts.post_content LIKE '%P110612%')
)
)
AND (prefix_posts.post_password = '')
AND (
( prefix_postmeta.meta_key = '_sku' AND prefix_postmeta.meta_value = 'P110612' )
OR mt1.post_id IS NULL
)
AND prefix_posts.post_type IN ('page', 'newsitem', 'partner', 'product')
AND ((prefix_posts.post_status = 'publish'))
GROUP BY prefix_posts.ID
ORDER BY prefix_posts.post_title LIKE '%P110612%' DESC, prefix_posts.post_date DESC

Comparing WP dates

I´m trying to show posts with a meta field "fecha_inicio" that contains a younger date with this format "dd/mm/yyyy" with 1 variable that also has a date with the same format "dd/mm/yyyy".
My code is the next one:
$args = array(
'posts_per_page' => 30,
'post_type' => 'programa',
'paged' => get_query_var( 'paged' ),
'meta_query' => array(
array(
'key' => 'fecha_inicio',
'value' => $fecha,
'type' => 'date',
'compare'=> '<'
),
),
);
This doesn´t return me anything. I have posts for example with date 10/10/2016 and 01/01/2016, and I´m trying the query with a date in the middle 05/05/2016, and It´s not returning me anything
I have printed the variable and shows the correct date, and also the other dates are correct on the database, dunno what I am missing here.
Also the field has the same name, that in the table post_meta
Trying to show the SQL Query
SELECT SQL_CALC_FOUND_ROWS
wp_posts.ID
FROM
wp_posts
INNER JOIN
wp_postmeta
ON
(
wp_posts.ID = wp_postmeta.post_id
)
WHERE
1 = 1 AND(
(
wp_postmeta.meta_key = 'fecha_inicio' AND CAST(wp_postmeta.meta_value AS DATE) > '01/05/2095'
)
) AND wp_posts.post_type = 'programa' AND(
wp_posts.post_status = 'publish' OR wp_posts.post_status = 'private'
)
GROUP BY
wp_posts.ID
ORDER BY
wp_posts.post_date
DESC
LIMIT 0, 30

Executing wrong relationship vlaue in $args in wp_Query?

i am trying to fetch data form database for my custom plugin and post type. my query arguments should be like
$args = array(
'post_type' => 'products',
'post_status'=> 'publish',
'meta_query' => array(
'relation' => 'OR',
array( 'key'=>'product_commercial',
'value'=>'on',
'compare'=>'='
),
array( 'key'=>'product_exterior',
'value'=>'on',
'compare'=>'='
)
)
);
$search_query = new WP_Query( $args );
But i am trying to add meta key values dynamically like :
$inner_arrays=array();
$count = 0;
foreach($values as $value){
if($value){
$inner_arrays[$count]['key'] .= $value;
$inner_arrays[$count]['value'] .= 'on';
$inner_arrays[$count]['compare'] .= '=';
$count++;
}
}
$args = array(
'post_type' => 'products',
'post_status'=> 'publish',
'meta_query' => array(
'relation' => 'OR',
$inner_arrays
)
);
//values are some random values (say fetched from db).
Now when i print the query using echo "<pre>Last SQL-Query: {$search_query->request}".'<br/>';
it displays
Last SQL-Query: SELECT SQL_CALC_FOUND_ROWS wp_posts.ID 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 ) WHERE 1=1 AND (
(
( wp_postmeta.meta_key = 'product_commercial' AND CAST(wp_postmeta.meta_value AS CHAR) = 'on' )
**AND**
( mt1.meta_key = 'product_framed' AND CAST(mt1.meta_value AS CHAR) = 'on' )
**AND**
( mt2.meta_key = 'product_horizontal' AND CAST(mt2.meta_value AS CHAR) = 'on' )
)
) AND wp_posts.post_type = 'products' AND ((wp_posts.post_status = 'publish')) GROUP BY wp_posts.ID ORDER BY wp_posts.post_date DESC LIMIT 0, 10
PROBLEM: i am using " relation => OR ", but getting "AND" in sql query. Where i am doing wrong?
$args['meta_query'] is assigned the following:
array(
'relation' => 'OR',
$inner_arrays
)
If you were to inspect this, e.g. by doing print_r( $args['meta_query'] );, you'd see:
Array
(
[relation] => OR
[0] => Array
(
[0] => Array
(
key => product_commercial,
value => on,
compare => =
),
[1] => Array
(
key => product_exterior,
value => on,
compare => =
)
)
)
In other words, the $inner_arrays array has itself become a sub-array of $args['meta_query']. As documented under Custom Field Parameters:
Important Note: meta_query takes an array of meta query arguments arrays (it takes an array of arrays) - you can see this in the examples below. This construct allows you to query multiple metadatas by using the relation parameter in the first (outer) array to describe the boolean relationship between the meta queries. Accepted arguments are 'AND', 'OR'. The default is 'AND'.
Consequently you are combining your elements of $inner_arrays using the default relation of AND, whilst then combining the result of that with any other members of $args['meta_query'] (there aren't any) using the explicit relation of OR.
You must instead either append each element of $inner_arrays to meta_query, or else just set $inner_arrays['relation'] = 'OR'.

I have query to retrieve all posts in wordpress but how can i check the condition for post meta_key =“develop” and meta_value='anything'

SELECT DISTINCT date(p.post_date) as post_date
FROM $wpdb->posts p
LEFT JOIN $wpdb->postmeta pm ON p.ID = pm.post_id
WHERE p.post_status='publish'
AND p.post_type IN ('%s')
ORDER BY p.post_date DESC
Here it reads all dates of the posts. My problem is that query should exclude the posts which has meta_value(it can be anything) of which meta_key="develop" . How can i write the query for that ?
I feel it will be in this form for wp_query
$args[ 'meta_query' ]= array (
'relation'= >' AND ',
array (
'key' = > 'respect'
),
array (
'key' => 'develop',
'compare' => 'NOT EXISTS'
)
);
$query = new Wp_query($args);
So you're trying to get all posts that don't have a meta key equal to "develop"?
You could do this:
$query = new WP_Query( array(
'meta_key' => 'develop',
'meta_compare' => 'NOT EXISTS'
) );
Also, see this note from the Codex if you're using a version of Wordpress less than 3.9:
"(Note: Due to bug #23268, value is required for NOT EXISTS comparisons to work correctly prior to 3.9. You must supply some string for the value parameter. An empty string or NULL will NOT work. However, any other string will do the trick and will NOT show up in your SQL when using NOT EXISTS. Need inspiration? How about 'bug #23268'.)"

Categories