SQL update row based on another row - mysql

I have a wordpress database table called "wp_postmeta"
It looks like this (simplified)
Here is what I want to do in english, hopefully someone can help me translate this to an actual SQL statement.
if meta_key(address) LIKE "%Portland%", update meta_value(post_city_id) to be 7,17" where the post_id's are the same.
So in the end it would look like:
I tried this, but no rows selected when executed.
UPDATE wp_postmeta
SET wp_postmeta_bak.meta_value = "7,17"
WHERE wp_postmeta.meta_key = "post_city_id"
AND
wp_postmeta.meta_key = "address" AND wp_postmeta.meta_value LIKE "%Portland"
I read in another questions here about using temporary table, but thats above my pay grade.

Ahhh, the mysterious and wonderful wp_postmeta key/value store sows more mystery and wonder. You need a self-join to correlate the postmeta rows that relate to the same post_id values as each other.
You need to update the right side of a self-joined table.
UPDATE wp_postmeta a
JOIN wp_postmeta b ON a.post_id = b.post_id
AND b.meta_key = 'address'
AND b.meta_value LIKE '%Portland%'
SET a.meta_value = '7,17'
WHERE a.meta_key = 'post_city_id'
The JOIN will yield an empty resultset unless all three of the ON criteria match. Once it yields a nonempty result set, it will update the correct rows.
Before you do this update, you can test that it's choosing the right rows with this query.
SELECT a.meta_id, a.post_id, a.meta_key, a.meta_value,
b.meta_value AS address
FROM wp_postmeta a
JOIN wp_postmeta b ON a.post_id = b.post_id
AND b.meta_key = 'address'
AND b.meta_value LIKE '%Portland%'
WHERE a.meta_key = 'post_city_id'
The rows in post_meta that will be updated have the meta_id values shown in this result set.

You're not getting any results because you're telling the code two different exclusive things:
1) where meta_key = "post_city_id" AND
2) where same column = "address".
From what I can see, it's either one or the other. You can't have both. It's probably a typo.
WHERE wp_postmeta.meta_key = "post_city_id"
AND
wp_postmeta.meta_key = "address"
AND wp_postmeta.meta_value LIKE "%Portland"

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 performance issues with big tables and joins

I have three tables, wp_posts(60000 records), wp_postmeta(130000 records) and news_news_obj(70000 records).
I want to find all the posts from news_news_obj table that are missing from the table wp_posts.
The comparison is made with news_news_obj.id with a custom field that every posts has in wp_postmeta table (oldpostid).
I tried with the 2 queries below first with a limit 30 and the one with NOT IN is faster from the one with the Joins.
The problem is that when I remove the LIMIT the query takes reaaaly too long.. I tried leaving it for a couple of hours and it didn't returned any results.
What can I do for this kind of problem and so big data?
Any help appreciated!
The first query with the joins:
SELECT meta2.id, meta2.title, meta2.main_text
FROM wp_posts
INNER JOIN wp_postmeta meta1 ON meta1.post_id = wp_posts.ID
AND meta1.meta_key = 'oldpostid'
AND wp_posts.post_type = 'post'
RIGHT JOIN news_news_obj meta2 ON meta1.meta_value = meta2.id
WHERE meta1.meta_value IS NULL
The second query I tried with NOT IN:
SELECT news_news_obj.id, news_news_obj.title, news_news_obj.main_text
FROM news_news_obj
WHERE news_news_obj.id NOT IN (
SELECT wp_postmeta.meta_value
FROM wp_posts, wp_postmeta
WHERE wp_posts.ID = wp_postmeta.post_id
AND wp_postmeta.meta_key = 'oldpostid'
AND wp_postmeta.meta_value = news_news_obj.id
AND wp_posts.post_status = 'publish'
AND wp_posts.post_type = 'post'
)
(See my Comments, plus...)
Indexes needed:
posts: INDEX(post_status, post_type, ID)
posts: INDEX(post_type, ID)
postmeta: PRIMARY KEY(post_id, meta_key)
The two queries will potentially get different results because only one has
AND wp_posts.post_status = 'publish'

Modifying a WP_Query Request join condition

I'm having an issue and have tried a lot of methods to solve it -
What I'm trying to do is modify a WordPress WP_Query before it runs: to query based on a post's parent ID rather than it's own ID. To be specific, I have a query that looks something like this:
SELECT SQL_CALC_FOUND_ROWS wp_posts.ID
FROM wp_posts
INNER JOIN wp_term_relationships
ON (wp_posts.ID = wp_term_relationships.object_id)
INNER JOIN wp_postmeta
ON ( wp_posts.ID = wp_postmeta.post_id )
WHERE 1=1 AND wp_posts.ID IN (1,2)
AND ( wp_term_relationships.term_taxonomy_id IN (35)
AND wp_posts.post_type = 'product_variation'
AND ((wp_posts.post_status = 'publish'))
GROUP BY wp_posts.ID
ORDER BY wp_postmeta.meta_value DESC LIMIT 0, 12;
The posts passed in are woocommerce product variations. The issue is that I want to return all product variations within the specific taxonomy, but the term_taxonomy_id of 35 is referenced only by the parent. So in the first join condition above, I believe I need:
ON(wp_posts.post_parent = wp_term_relationship.object_id)
Should be easy enough, but I can't figure out a way to modify this query suitably before it runs. Here are some things I have tried:
The tax_query has a primary_id_column that seems like it would be the right value to modify. I tried modifying the args before creating the query like this:
$args['tax_query']['primary_id_column'] = 'post_parent';
$wp_query = new WP_Query( $args );
In this case, the query vars are not modified whatsoever. I also tried several ways of modifying primary_id_column after the object is created, like these:
$wp_query->tax_query->primary_id_column = 'post_parent';
$wp_query->tax_query->get_sql('wp_posts', 'post_parent');
$wp_query->set('primary_id_column','post_parent');
These do in fact modify the query vars, but no matter what - the $wp_query->request string always contains the join condition on wp_posts.ID rather than wp_posts.post_parent. I wondered if at the point I make these changes the query has already been generated and isn't changed before I get the posts. I tried running the above lines in a hook for this reason, using:
add_action( 'pre_get_posts', 'custom_use_parent' );
But no luck. If anybody has a suggestion for how I could modify the join condition in this query, it would be greatly appreciated! Thanks a ton in advance.

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.

How do I select from multiple rows in wp_postmeta table using wpdb select statement Wordpress

I have a select statement like this:
$results = $wpdb->get_results( "SELECT $wpdb->postmeta.meta_value
FROM $wpdb->postmeta
WHERE 1=1 AND $wpdb->postmeta.meta_key = 'geo_short_address'" );
But I'm now trying to filter this statement to only include those 'geo_short_address' that also have an entry or not empty in a meta-key field called '_Company'
They have the same post id, for instance:
post_id meta_key meta_value
53 geo_short_address Nottingham
53 _Company Ledgemonkey
So I only want to return Nottingham if the meta_key _Company exists in the post_id
There are other entries in the DB that will not have the meta_key _Company they are the ones I want to exclude ...?
I have tried various things but can't seem to get the combination..?
To restate your question, you want to filter the postmeta table on multiple criteria. You need to do something like this (edit to include meta_key criteria).
SELECT whatever
FROM $wpdb->postmeta.meta_value
WHERE 1=1
AND $wpdb->postmeta.post_id IN (
SELECT distinct $wpdb->postmeta.post_id
FROM $wpdb->postmeta
WHERE $wpdb->postmeta.post_value = 'Ledgemonkey'
AND $wpdb->postmeta.meta_key = '_Company'
)
AND $wpdb->postmeta.meta_key = 'geo_short_address'
This can be a bit of a performance hairball if you have tens of thousands of postmeta rows, but it will work.
By the way, you can get rid of that ugly but inconsequential 1=1 by judicious use of the implode() function.