very slow query with ORDER BY - mysql

I have read through many tutorials online and here on stackoverflow but I still can't figure out how to solve the problem I'm facing right now.
I would like to tell you guys that I'm a mysql newbie so please forgive my noobness.
Alright, the query is this and it grabs the information that I need from wordpress database
SELECT
product.ID productId,
product.guid productLink,
product.post_title productTitle,
post.ID postId,
post.post_title postTitle,
post.post_content postContent,
post.post_date postDate,
tm.slug typeSlug, tm.name typeName,
tm2.slug langSlug, tm2.name langName,
tm3.slug pubSlug, tm3.name pubName,
IFNULL(wl.id,0) wishlist
FROM wp_posts product
JOIN wp_postmeta meta ON meta.meta_key = 'p2m' AND meta.meta_value=product.ID
JOIN wp_posts post ON post.ID = meta.post_id
JOIN wp_term_relationships tr ON tr.object_id = product.ID
JOIN wp_term_taxonomy tt ON tt.term_taxonomy_id = tr.term_taxonomy_id AND tt.taxonomy = 'mtype'
JOIN wp_terms tm ON tm.term_id = tt.term_id
JOIN wp_term_relationships tr2 ON tr2.object_id = product.ID
JOIN wp_term_taxonomy tt2 ON tt2.term_taxonomy_id = tr2.term_taxonomy_id AND tt2.taxonomy = 'language'
JOIN wp_terms tm2 ON tm2.term_id = tt2.term_id
JOIN wp_term_relationships tr3 ON tr3.object_id = product.ID
JOIN wp_term_taxonomy tt3 ON tt3.term_taxonomy_id = tr3.term_taxonomy_id AND tt3.taxonomy = 'publisher'
JOIN wp_terms tm3 ON tm3.term_id = tt3.term_id
LEFT JOIN wp_yith_wcwl wl ON wl.user_id = 1 AND wl.prod_id = product.ID AND wl.post_id = post.ID
WHERE product.post_type = 'product'
ORDER BY post.post_date DESC LIMIT 0,35
When I remove "ORDER BY post.post_date DESC" the speed of the query gets down to .03 seconds which is freaking amazing.. But with the addition of the "ORDER BY post.post_date DESC" the speed of the query goes to amazing 10+ seconds which is way too long..
I've used EXPLAIN and it seems that there is usage of filesort when the ORDER BY by date gets into the query.
I need to have my query reply back the results according to the post_date so I can't figure out what I could do at this point...
Additionally, I would like to point it out that in Database Description of wordpress there is an INDEX referred as "type_status_date" which could be used in my case. However, I'm totally clueless where to use it and how to do it. If there is anyone who can point out the flaw in the logic of my query or help me out with the optimization of the query (or index) please do so. Thanks for you kind attention!
P.S: I don't know how to create an index too :)
Initial Result of EXPLAIN with ORDER BY

JOIN wp_postmeta meta
ON meta.meta_key = 'p2m' -- filters
AND meta.meta_value=product.ID -- shows relation
is confusing. JOIN...ON is used to say how two tables are related. Filters belong in WHERE:
WHERE ...
AND meta.meta_key = 'p2m'
...
wp_postmeta is not well indexed. More discussion here .
Adding INDEX(post_date) may or may not help performance -- It depends on how quickly 35 good rows are found.
From the EXPLAIN, we see that the worst part is getting into meta -- something like 30K rows to look through. This _estimates that there are 30 rows with meta_key = 'p2m'. How many rows are there?
Unfortunately wp_postmeta is not designed to efficiently start with the meta_key+meta_value. This is a general problem with key-value stores (such as Posts in WP), especially when the 'value' is LONGTEXT.

The index on wp_post for type_status_date, has the date field as the third field of the index,
type_status_date INDEX
post_type
post_status
post_date
ID
So you have a predicate for post type, but post status is not included in your query in any predicate, at best it can therefore do a partial index scan of the index (sometimes called a skip scan or range scan depending on the db, my sql is not going to play ball easily on that) but it will be slower.
That is a best case scenario though, with all those joins and additional fields not covered by the index the cost of the index scan and row lookups could be far too high to consider even touching the index vs a straight scan.
It would help if you post the explain plan, it would help confirm what the optimizer was doing. The above is a more generic DB engine commentary.

type_status_date is a combined index so it's used only if you order by all it's components. It cannot be used by MySQL to order by only post_date. So the best solution is to add an index by post_date.

Related

Table MYSQL query

I have used WPAllImport to import data from a CSV-file to Advanced Custom Fields.
I now want to put them back together with a SQL query, but dont know how to do it.
I've tried WPDataTables, but when I choose 5 or more tables, WPDataTables stops.
If I pick 2, I get this code
SELECT posts_podukter.post_title AS podukter_post_title,
podukter_meta_produkter_0_pris_tbl.meta_value AS podukter_meta_produkter_0_pris
FROM beta_h3L_posts AS posts_podukter
INNER JOIN (SELECT podukter_meta_produkter_0_pris_tbl_posts.ID as id, meta_value, meta_key FROM beta_h3L_postmeta AS podukter_meta_produkter_0_pris_tbl_postmeta INNER JOIN beta_h3L_posts AS podukter_meta_produkter_0_pris_tbl_posts ON podukter_meta_produkter_0_pris_tbl_postmeta.post_id = podukter_meta_produkter_0_pris_tbl_posts.ID AND podukter_meta_produkter_0_pris_tbl_posts.post_type = 'podukter') AS podukter_meta_produkter_0_pris_tbl
ON podukter_meta_produkter_0_pris_tbl.meta_key = 'produkter_0_pris' AND podukter_meta_produkter_0_pris_tbl.id = posts_podukter.ID
WHERE 1=1
AND posts_podukter.post_type = 'podukter'
I think this is too much code.
Can someone help me to get on the right way.... :-)
This is what the table should look like
Here is a capture how the table should look like
I would agree that this is "too much code" which sounds sort of ridiculous, but in this case totally applies. That SQL statement that was produced could be written as:
SELECT
post.post_title as podukter_post_title,
postmeta.meta_value as podukter_meta_produkter_0_pris
FROM beta_h3L_posts AS posts
INNER JOIN beta_h3L_postmeta AS postmeta
ON postmeta.post_id = post.ID
AND postmeta.meta_key = 'produkter_0_pris'
WHERE posts.post_type = 'podukter'
If there is another metavalue that you need you can join again to your meta table:
SELECT
post.post_title as podukter_post_title,
postmeta.meta_value as podukter_meta_produkter_0_pris,
postmeta2.meta_value as tilbudspris
FROM beta_h3L_posts AS posts
INNER JOIN beta_h3L_postmeta AS postmeta
ON postmeta.post_id = post.ID
AND postmeta.meta_key = 'produkter_0_pris'
INNER JOIN beta_h3L_postmeta AS postmeta2
ON postmeta.post_id = post.ID
AND postmeta2.meta_key = 'tilbudspris'
WHERE posts.post_type = 'podukter'
I don't know what any of these words mean (besides post and postmeta) so I'm just going to assume that this is right/helpful.
The only thing is that you may want to switch to using a LEFT OUTER JOIN to your postmeta table just in case the meta_key you are after doesn't exist for the post.id you are querying. In that case, with an INNER JOIN the id/post will be dropped from the result set where a LEFT OUTER JOIN will show the id/post record with a blank for whatever that corresponding meta_value is that you are joining in.

MySQL very long queries

I have an quite large db (800+Mb dump file) that I imported to my local server. Its a Wordpress db from witch i need to extract certain posts. There is around 160000 posts inside.
Currently im testing a bit with MySql Workbench running simple queries with JOIN and it requires a lot of time, so long actually that Workbench stops processing.
Here`s an example:
SELECT
COUNT(*)
FROM wp_posts
LEFT JOIN wp_term_relationships
ON wp_posts.ID = wp_term_relationships.object_id
LEFT JOIN wp_term_taxonomy
ON wp_term_relationships.term_taxonomy_id = wp_term_taxonomy.term_taxonomy_id
LEFT JOIN wp_terms
ON wp_term_taxonomy.term_id = wp_terms.term_id
WHERE wp_terms.term_id = 195;
Running over 600 seconds.
Here is an wordpress db schema:
Of course chance is I'm just bad at SQL, not really my field...
If you have all indexes on columns you use for joining (wp_posts.ID, wp_term_relationships.object_id etc.), this should not be a problem, and query should be executed in less than 1 sec (1 sec is a lot, too).
Also, there might be waiting on other queries in queue (locks), so you should add this to those queries:
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED ;
SELECT
COUNT(*)
FROM
wp_posts
LEFT JOIN
wp_term_relationships ON wp_posts.ID = wp_term_relationships.object_id
LEFT JOIN
wp_term_taxonomy ON wp_term_relationships.term_taxonomy_id = wp_term_taxonomy.term_taxonomy_id
LEFT JOIN
wp_terms ON wp_term_taxonomy.term_id = wp_terms.term_id
WHERE
wp_terms.term_id = 195;
COMMIT ;
Create indexes in workbench. I can see here that you are joining on IDs on every table, so because those are ID columns, they should be PRIMARY KEY, and create UNIQUE Clustered index on them.
If you are really just running a count you can get your result with:
SELECT COUNT(*)
FROM wp_term_relationships wtr
JOIN wp_term_taxonomy wtt
ON wtt.term_taxonomy_id = wtr.term_taxonomy_id
WHERE wtt.term_id = 195;
You don't need the information from the obligatory wp_terms or wp_posts records that are attached to these records.
You also don't need a LEFT JOIN as you are discarding NULL tuples with your WHERE condition.
As for the speed, I would suggest running the query prepended with EXPLAIN and checking that everything is indexed correctly and these indices are being used.

Troubleshooting Wordpress/Woocommerce custom SQL query for reporting

Hopefully this is the right forum, my question seems to overlap the stack exchange community so this seemed best.
I have some custom reports for my WooCommerce orders on my wordpress site. I have one query that is just freezing locally, meaning in my localhost my CPU goes to 100% and it never finishes and I don't understand why. To the point here is the query:
SELECT SUM(postmeta.meta_value)
FROM pca_postmeta AS postmeta
LEFT JOIN pca_woocommerce_order_items AS orders ON orders.order_id = postmeta.post_id
WHERE postmeta.meta_key = '_order_total'
AND orders.order_item_id IN (
SELECT item_meta.order_item_id
FROM pca_woocommerce_order_itemmeta AS item_meta
LEFT JOIN pca_woocommerce_order_items AS orders ON item_meta.order_item_id = orders.order_item_id
LEFT JOIN pca_posts AS posts ON posts.ID = orders.order_id
WHERE item_meta.meta_value = '23563'
AND posts.post_status IN ('wc-processing','wc-completed')
GROUP BY orders.order_id
)
As you can hopefully see the goal here is to get the summation of all orders from this specific campaign (23563). The nested query works exactly as expected on its own, returning just a list of IDs like so:
NOTE: little curious if 2.6289 secs is long when it only returned 65 total, although there are 148220 total
The problem is this query doesn't seem to like the nested part. Any suggestions? Completely different approach in mind?
P.S. I use that nested query at other times as well to represent all orders by campaign id in my php reporting class. But for my question PHP has nothing to do with it.
UPDATE/FOLLOW UP:
Is it possible to convert this into a join as described here: Using a SELECT statement within a WHERE clause ? I'm a little light on my SQL so not sure how I would do that but it seems promising
GROUP BY orders.order_id
does not make sense because you are selecting only order_item_id.
pca_woocommerce_order_itemmeta would benefit from
INDEX(meta_value, order_item_id)
An this might be an equivalent query, but avoiding the IN(SELECT...):
SELECT SUM(pm.meta_value)
FROM
( SELECT im.order_item_id
FROM pca_woocommerce_order_itemmeta AS im
LEFT JOIN pca_woocommerce_order_items AS o
ON im.order_item_id = o.order_item_id
LEFT JOIN pca_posts AS posts ON posts.ID = o.order_id
WHERE im.meta_value = '23563'
AND posts.post_status IN ('wc-processing','wc-completed')
GROUP BY o.order_id
) AS w
JOIN pca_woocommerce_order_items AS o ON w.order_item_id = o.order_item_id
JOIN pca_postmeta AS pm ON o.order_id = pm.post_id
WHERE pm.meta_key = '_order_total'
Edit
Some principles behind what I did. Here I am guessing at what the optimizer will do with various possible formulations of the query.
I got rid of LEFT -- This may have changed the output. But I needed to avoid LEFT JOIN ( SELECT ... ) which would not be optimizable.
By having one subquery in the list of "tables" being JOINed, the optimizer will (almost certainly) start with the subquery and do "Nested Loop Joins" to the other tables. NLJ is the common way to perform a query.
A subselect like that has no index, so it needs to be first in the order, else it will be very inefficient.
Without subqueries, the optimizer generally likes to start with whichever table has something in the WHERE clause.
The requirement to start with the subquery "table" is stronger than the desire to pick the table based on WHERE pm.meta_key = '_order_total'.
Inside the subquery, the only "=" test (WHERE im.meta_value = '23563) provides the likely starting point for that set of JOINs. This is further enhanced by it not being 'right' of a LEFT JOIN. Hence, I suggested that index.

mysql slow queries and timeout in wordpress

I am not an expert in sql.
My wordpress started to return timeouts and respond really slow.
when I started digging, I noticed that the slow_query log has a lot to tell me.
unfortunately I have a lot of slow queries.
for example:
# Time: 140425 17:03:29
# User#Host: geektime[geektime] # localhost []
# Query_time: 7.024031 Lock_time: 0.000432 Rows_sent: 0 Rows_examined: 0
SET timestamp=1398434609;
SELECT 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)
LEFT JOIN wp_postmeta AS order1 ON order1.post_id = wp_posts.ID
AND order1.meta_key = '_event_start_date'
LEFT JOIN wp_postmeta AS order2 ON order2.post_id = wp_posts.ID
AND order2.meta_key = '_event_start_time'
WHERE 1=1
AND wp_posts.post_type = 'event'
AND (wp_posts.post_status = 'publish'
OR wp_posts.post_status = 'future'
OR wp_posts.post_status = 'draft'
OR wp_posts.post_status = 'pending')
AND ((wp_postmeta.meta_key = '_event_start_date'
AND CAST(wp_postmeta.meta_value AS CHAR) BETWEEN '2014-04-11' AND '2014-04-17')
OR (mt1.meta_key = '_event_end_date'
AND CAST(mt1.meta_value AS CHAR) BETWEEN '2014-04-11' AND '2014-04-17'))
GROUP BY wp_posts.ID
ORDER BY order1.meta_value,
order2.meta_value ASC;
The columns post_id, meta_id and meta_key are indexed in wp_postmeta table.
The columns ID, post_name, post_type, post_status, post_date,post_parent, post_author and guid are indexed in wp_posts table.
however, the columns ID and GUID are indexed twice, is it bad?
and there are 4 indexs with the same key_name: type_status_date, is it bad?
How could it be that I have 60K rows in wp_posts and 3M rows in wp_postmeta?
I know its a lot to ask but I really tried to understand from researching online.
thanks in advance.
however, the columns ID and GUID are indexed twice, is it bad?
There are two different columns, so no, unless you're meaning that both have two indexes on them — in which case yes, it's bad and likely a bug in one of your theme or plugins (or a prior bug in WP itself).
and there are 4 indexs with the same key_name: type_status_date, is it bad?
Same as above: if you mean four identical indexes, it's either a theme or plugin or WP bug and you can safely drop the duplications.
How could it be that I have 60K rows in wp_posts and 3M rows in wp_postmeta?
Because the WP meta API sucks and enforces a database anti-pattern called the Entity Attribute Value (also known as EAV):
http://en.wikipedia.org/wiki/Entity-attribute-value_model
Cursory googling SO will yield plenty of threads that explain why it is a bad idea to store data in an EAV or equivalent (json, hstore, xml, whatever) if the stuff ever needs to appear in e.g. a where, join or order by clause.
You can see the inefficiencies first-hand in form of the slow query you highlighted. The query is joining the meta table four times, does so twice with a cast operator to boot — and it casts the value to char instead of date at that. Adding insult to injury, it then proceeds to order rows using values stored within it. It is a recipe for poor performance.
There is, sadly, little means of escaping the repulsive stench of this sewage, short of writing your own plugins that create proper tables to store, index and query the data you need in lieu of using the WP meta API, its wretched quoting madness, and the putrid SQL that results from using it.
One thing that you can do as temporary duct tape and WD-40 measure while you rewrite the plugins you're using from the ground up, is to toss callbacks on one or more of the filters you'll find in the giant mess of a class method that is WP_Query#get_posts(). For instance the posts_request filter, which holds the full and final SQL query, allows you to rewrite anything to your liking using regex-foo. It's no magic bullet: doing so will allow you to fix bugs such as integer values getting sorted lexicographically and such, as well as toss in very occasional query optimizations; little more.
Edit: Upon re-reading your query, methinks you're mostly in luck with respect to that last point. Your particular query features the following abomination:
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)
LEFT JOIN wp_postmeta AS order1 ON order1.post_id = wp_posts.ID
AND order1.meta_key = '_event_start_date'
LEFT JOIN wp_postmeta AS order2 ON order2.post_id = wp_posts.ID
AND order2.meta_key = '_event_start_time'
Two of those have _event_start_date in common, so you can factor it out:
SELECT wp_posts.*
FROM wp_posts
INNER JOIN wp_postmeta ON (wp_posts.ID = wp_postmeta.post_id)
AND wp_postmeta.meta_key = '_event_start_date'
INNER JOIN wp_postmeta AS mt1 ON (wp_posts.ID = mt1.post_id)
AND mt1.meta_key = '_event_end_date'
INNER JOIN wp_postmeta AS order2 ON order2.post_id = wp_posts.ID
AND order2.meta_key = '_event_start_time'
WHERE 1=1
AND wp_posts.post_type = 'event'
AND (wp_posts.post_status = 'publish'
OR wp_posts.post_status = 'future'
OR wp_posts.post_status = 'draft'
OR wp_posts.post_status = 'pending')
AND (CAST(wp_postmeta.meta_value AS CHAR) BETWEEN '2014-04-11' AND '2014-04-17'
OR CAST(mt1.meta_value AS CHAR) BETWEEN '2014-04-11' AND '2014-04-17')
GROUP BY wp_posts.ID
ORDER BY wp_postmeta.meta_value,
order2.meta_value ASC;
Among other things, slow performance is caused by the use of functions like this:
AND CAST(wp_postmeta.meta_value AS CHAR) BETWEEN '2014-04-11' AND '2014-04-17')
Assuming that field is a date field, you will get better performance with something like this:
and wp_postmeta.meta_value >= AStartDateVariable
and wp_postmeta.meta_value < TheDayAfterAnEndDateVariable
That will be even more true if meta_value is indexed. I assume you will be sending these variables as query parmameters.
Holy cow! 3 megarows in postmeta? 60k posts? Something is seriously wrong with your installation.
Is it possible that your events table is open to spammers entering rubbish?
Do you have tons of old expired events that could somehow be purged from your system?
You may be able to get your system back on the air by increasing your timeout value. If you know how to handle php.ini, go find the timeout value and increase it, or ask your hosting company for help.
Are you on one of those $5 per month hosting companies? With sixty thousand events to handle, you may need to upgrade.
The proximate cause of the timeout is obvious. This sequence of code is full-scanning that monster post_meta table TWICE!
Why? It has an OR in it. And it is applying functions to the value of a column.
AND ((wp_postmeta.meta_key = '_event_start_date'
AND CAST(wp_postmeta.meta_value AS CHAR) BETWEEN '2014-04-11' AND '2014-04-17')
OR (mt1.meta_key = '_event_end_date'
AND CAST(mt1.meta_value AS CHAR) BETWEEN '2014-04-11' AND '2014-04-17'))
One of the disadvantages of the WordPress schema when you scale up a site is the generic nature of the postmeta table. This query does date range searches, but it's hard to index a key-value repository like postmeta to optimize those.
Do you know your way around the code of the Events Manager plugin you're using? If so, you may want to investigate optimizing this yourself.
If not, seek support from the Events Manager plugin developer.

What does "using join buffer" means in the Explain table of an MySQL query?

I have the following complex query and I notice that it is making my website so slow I want to find a way to optimize it:
SELECT tahminler.result,
tahminler.tahmin,
tahminler.match_id,
tahminler.timestamp,
tahminler.tahmin_text,
users.username,
matches_of_comments.tournament_id,
matches_of_comments.match_status,
matches_of_comments.match_date,
matches_of_comments.localteam_name,
matches_of_comments.visitorteam_name,
matches_of_comments.localteam_id,
matches_of_comments.visitorteam_id,
matches_of_comments.localteam_goals,
matches_of_comments.visitorteam_goals,
new_iddaa.iddaa_code,
tahminler_results.ms1,
tahminler_results.ms2,
tahminler_results.ms0,
tahminler_results.alt,
tahminler_results.ust,
tahminler_results.tg_0_1,
tahminler_results.tg_2_3,
tahminler_results.tg_4_6,
tahminler_results.tg_7,
tahminler_results.kg_var,
tahminler_results.kg_yok,
tahmins.tahmin as text_tahmin
FROM tahminler
INNER JOIN users on users.id = tahminler.user_id
INNER JOIN matches_of_comments on tahminler.match_id = matches_of_comments.match_id
Left JOIN new_iddaa on new_iddaa.match_id = matches_of_comments.match_id
INNER JOIN tahmins on tahminler.tahmin = tahmins.id
LEFT JOIN tahminler_results on tahminler.match_id = tahminler_results.match_id
Where tahminler.user_id = $user_id
order by tahminler.timestamp DESC
I do not have much experience in databases or optimization so I did an Explain for this query and i got this table :
I think the problem in the row which tells "using join buffer" but what does that mean ??
can you help me to understand this point and making the query optimized ?
I'd recommend checking for indices, especially on the columns you want to join on. Do you have an index on matches_of_comments.match_id? Also, an index on tahmins.id seems to be missing.