I'm querying the WordPress wp_postmeta table for the lowest meta_value of rows with the meta_key item_thickness:
SELECT min(cast(meta_value as unsigned)) FROM wp_postmeta WHERE meta_key='item_thickness'
This works great.
Question: How would I extend this query to select the same lowest item_thickness from rows with the same post_id and with meta_key='item_status' and meta_value='Raw'
The post_id forms the relationship between these rows but I don't know how to do a JOIN on the same table or the proper syntax for a sub-query
This is my latest (failing) attempt at the query:
SELECT *
FROM wp_postmeta
JOIN (
SELECT min(cast(meta_value as unsigned)), post_id FROM wp_postmeta WHERE meta_key='item_thickness'
) b
ON wp_postmeta.post_id=b.post_id
I was able to use WP_Query to build the MySQL I needed and then edit it so it would select the value I wanted.
Working query.
$wpdb->get_var( "SELECT min(cast(wp_postmeta.meta_value as unsigned)) FROM wp_postmeta INNER JOIN wp_posts 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_postmeta.meta_key = 'item_thickness' AND ( mt1.meta_key = 'item_status' AND CAST(mt1.meta_value AS CHAR) = 'raw' )) AND wp_posts.post_type = 'inventory' AND (wp_posts.post_status = 'publish' OR wp_posts.post_status = 'future' OR wp_posts.post_status = 'draft' OR wp_posts.post_status = 'pending' OR wp_posts.post_status = 'private')" );
Related
The following does not necessarily use correct syntax, but is intended to convey my intention:
DELETE FROM wp_posts, wp_postmeta
WHERE wp_posts.ID <> min(f.post_ID), wp_postmeta.post_id <> min(f.post_ID)
(It's really two deletes combined in one, with the WHERE condition for the first and second table respectively.)
where min(f.post_ID) comes from the virtual table below:
SELECT Min(f.post_id),
f.post_title,
f.meta_value
FROM (SELECT wp_posts.post_title,
Min(wp_postmeta.meta_value) AS minprice
FROM wp_postmeta
JOIN wp_posts
ON wp_postmeta.post_id = wp_posts.id
WHERE wp_posts.post_type = 'Product'
AND wp_postmeta.meta_key = '_regular_price'
GROUP BY wp_posts.post_title) AS x
INNER JOIN (SELECT wp_postmeta.post_id,
wp_posts.post_title,
wp_postmeta.meta_value
FROM wp_postmeta
JOIN wp_posts
ON wp_postmeta.post_id = wp_posts.id
WHERE wp_posts.post_type = 'Product'
AND wp_postmeta.meta_key = '_regular_price'
ORDER BY wp_posts.post_title,
wp_postmeta.meta_value) AS f
ON x.post_title = f.post_title
AND f.meta_value = x.minprice
GROUP BY f.post_title
How would the query look?
Edit: It's worth considering that any implementation that deletes from one table before the other will change the outcome of the second delete. (The second DELETE will be affected by the first delete because items from the table in the sub query were deleted in the first DELETE.)
You don't delete from two tables at the same time. If the tables are related, you delete from the child, then the parent. If they're unrelated the delete may happen in any order. If they're unrelated but have other tables depending on them (i.e. they are parents themselves and have children) then data must be cleared out of those other tables first. If the relational constraints are set to CASCADE DELETE mode, then child table data will be deleted automatically when parent table data is deleted. If the delete must happen as an all or nothing affair (i.e. if the second delete fails after the first delete succeeds you don't want the first delete to succeed), it should be done in a transaction.
Thus:
DELETE FROM wp_postmeta WHERE post_id NOT IN (
SELECT Min(f.post_id)
FROM (SELECT wp_posts.post_title,
Min(wp_postmeta.meta_value) AS minprice
FROM wp_postmeta
JOIN wp_posts
ON wp_postmeta.post_id = wp_posts.id
WHERE wp_posts.post_type = 'Product'
AND wp_postmeta.meta_key = '_regular_price'
GROUP BY wp_posts.post_title) AS x
INNER JOIN (SELECT wp_postmeta.post_id,
wp_posts.post_title,
wp_postmeta.meta_value
FROM wp_postmeta
JOIN wp_posts
ON wp_postmeta.post_id = wp_posts.id
WHERE wp_posts.post_type = 'Product'
AND wp_postmeta.meta_key = '_regular_price'
ORDER BY wp_posts.post_title,
wp_postmeta.meta_value) AS f
ON x.post_title = f.post_title
AND f.meta_value = x.minprice
GROUP BY f.post_title
)
DELETE FROM wp_posts WHERE ID NOT IN (
SELECT Min(f.post_id)
FROM (SELECT wp_posts.post_title,
Min(wp_postmeta.meta_value) AS minprice
FROM wp_postmeta
JOIN wp_posts
ON wp_postmeta.post_id = wp_posts.id
WHERE wp_posts.post_type = 'Product'
AND wp_postmeta.meta_key = '_regular_price'
GROUP BY wp_posts.post_title) AS x
INNER JOIN (SELECT wp_postmeta.post_id,
wp_posts.post_title,
wp_postmeta.meta_value
FROM wp_postmeta
JOIN wp_posts
ON wp_postmeta.post_id = wp_posts.id
WHERE wp_posts.post_type = 'Product'
AND wp_postmeta.meta_key = '_regular_price'
ORDER BY wp_posts.post_title,
wp_postmeta.meta_value) AS f
ON x.post_title = f.post_title
AND f.meta_value = x.minprice
GROUP BY f.post_title
)
Warning
Don't run a delete query given to you by someone on the internet without backing your data up first. At the very least, start a transaction, run the delete, select the results and look at them to ensure they're correct, using the following code pattern:
START TRANSACTION;
DELETE FROM ...
DELETE FROM ...
SELECT * FROM ... -/*to check the deletes worked and didn't remove too much*/
ROLLBACK;
Change the ROLLBACK to COMMIT when you're happy
Edit:
Option 1
Make the posts meta depend on posts (it may do already, ensure the deletes are cascaded):
ALTER TABLE posts_meta
ADD CONSTRAINT fk_pm FOREIGN KEY (posts_id) REFERENCES posts(id) ON DELETE CASCADE
Now run your delete on the posts table, posts_meta entries will also disappear
Option2
Run your delete on posts table as recommended above
Use the following query to delete any record from posts_meta that doesnt have a matching record in posts:
DELETE FROM posts_meta WHERE post_id IN (select post_id from(
SELECT pm.post_id
FROM
posts_meta pm
LEFT JOIN
posts p
ON p.id = pm.post_id
WHERE
p.id IS NULL
) i )
The innermost subquery that finds the id list is wrapped inside another subquery for a reason; there are situations where MySQL will refuse a delete if the pattern is DELETE FROM x WHERE y IN (SELECT x FROM y) because you can't modify a table you're selecting from. Wrapping it up in another select is a hack that causes MySQL to not treat it as deleting from the same table you're selecting from
The idea is to select IDs to be deleted from tables wp_posts and wp_postmeta into temporary tables posts and postmeta. This will preserve a list of IDs even if you delete data from any table. Then delete data from tables wp_posts and wp_postmeta one after another based on the list of IDs in the temp tables. The last step is to clear temporary tables (delete data from them).
The transaction is used in that way to allow run this code in PHPMyAdmin.
The optimal version of the code:
BEGIN;
CREATE TEMPORARY TABLE IF NOT EXISTS minPostIds AS
SELECT Min(f.post_id)
FROM (SELECT wp_posts.post_title,
Min(wp_postmeta.meta_value) AS minprice
FROM wp_postmeta
JOIN wp_posts
ON wp_postmeta.post_id = wp_posts.id
WHERE wp_posts.post_type = 'Product'
AND wp_postmeta.meta_key = '_regular_price'
GROUP BY wp_posts.post_title) AS x
INNER JOIN (SELECT wp_postmeta.post_id,
wp_posts.post_title,
wp_postmeta.meta_value
FROM wp_postmeta
JOIN wp_posts
ON wp_postmeta.post_id = wp_posts.id
WHERE wp_posts.post_type = 'Product'
AND wp_postmeta.meta_key = '_regular_price') AS f
ON x.post_title = f.post_title
AND f.meta_value = x.minprice
GROUP BY f.post_title;
DELETE FROM wp_posts WHERE wp_posts.ID IN (SELECT ID FROM minPostIds);
DELETE FROM wp_postmeta WHERE wp_postmeta.post_id IN (SELECT ID FROM minPostIds);
DELETE FROM minPostIds;
COMMIT;
This version works:
BEGIN;
CREATE TEMPORARY TABLE IF NOT EXISTS posts AS
SELECT ID
FROM wp_posts
WHERE wp_posts.ID NOT IN
(
SELECT Min(f.post_id)
FROM (SELECT wp_posts.post_title,
Min(wp_postmeta.meta_value) AS minprice
FROM wp_postmeta
JOIN wp_posts
ON wp_postmeta.post_id = wp_posts.id
WHERE wp_posts.post_type = 'Product'
AND wp_postmeta.meta_key = '_regular_price'
GROUP BY wp_posts.post_title) AS x
INNER JOIN (SELECT wp_postmeta.post_id,
wp_posts.post_title,
wp_postmeta.meta_value
FROM wp_postmeta
JOIN wp_posts
ON wp_postmeta.post_id = wp_posts.id
WHERE wp_posts.post_type = 'Product'
AND wp_postmeta.meta_key = '_regular_price'
ORDER BY wp_posts.post_title,
wp_postmeta.meta_value) AS f
ON x.post_title = f.post_title
AND f.meta_value = x.minprice
GROUP BY f.post_title
);
CREATE TEMPORARY TABLE IF NOT EXISTS postmeta AS
SELECT post_id
FROM wp_postmeta
WHERE wp_postmeta.post_id NOT IN
(
SELECT Min(f.post_id)
FROM (SELECT wp_posts.post_title,
Min(wp_postmeta.meta_value) AS minprice
FROM wp_postmeta
JOIN wp_posts
ON wp_postmeta.post_id = wp_posts.id
WHERE wp_posts.post_type = 'Product'
AND wp_postmeta.meta_key = '_regular_price'
GROUP BY wp_posts.post_title) AS x
INNER JOIN (SELECT wp_postmeta.post_id,
wp_posts.post_title,
wp_postmeta.meta_value
FROM wp_postmeta
JOIN wp_posts
ON wp_postmeta.post_id = wp_posts.id
WHERE wp_posts.post_type = 'Product'
AND wp_postmeta.meta_key = '_regular_price'
ORDER BY wp_posts.post_title,
wp_postmeta.meta_value) AS f
ON x.post_title = f.post_title
AND f.meta_value = x.minprice
GROUP BY f.post_title
);
DELETE FROM wp_posts WHERE wp_posts.ID IN (SELECT ID FROM posts);
DELETE FROM wp_postmeta WHERE wp_postmeta.post_id IN (SELECT post_id FROM postmeta);
DELETE FROM posts;
DELETE FROM postmeta;
COMMIT;
You can also create a stored procedure:
CREATE PROCEDURE DeleteFromTables()
BEGIN
CREATE TEMPORARY TABLE IF NOT EXISTS posts
ENGINE=MyISAM
AS (
SELECT ID
FROM wp_posts
WHERE wp_posts.ID NOT IN
(
SELECT Min(f.post_id)
FROM (SELECT wp_posts.post_title,
Min(wp_postmeta.meta_value) AS minprice
FROM wp_postmeta
JOIN wp_posts
ON wp_postmeta.post_id = wp_posts.id
WHERE wp_posts.post_type = 'Product'
AND wp_postmeta.meta_key = '_regular_price'
GROUP BY wp_posts.post_title) AS x
INNER JOIN (SELECT wp_postmeta.post_id,
wp_posts.post_title,
wp_postmeta.meta_value
FROM wp_postmeta
JOIN wp_posts
ON wp_postmeta.post_id = wp_posts.id
WHERE wp_posts.post_type = 'Product'
AND wp_postmeta.meta_key = '_regular_price'
ORDER BY wp_posts.post_title,
wp_postmeta.meta_value) AS f
ON x.post_title = f.post_title
AND f.meta_value = x.minprice
GROUP BY f.post_title
)
);
CREATE TEMPORARY TABLE IF NOT EXISTS postmeta
ENGINE=MyISAM
AS (
SELECT post_id
FROM wp_postmeta
WHERE wp_postmeta.post_id NOT IN
(
SELECT Min(f.post_id)
FROM (SELECT wp_posts.post_title,
Min(wp_postmeta.meta_value) AS minprice
FROM wp_postmeta
JOIN wp_posts
ON wp_postmeta.post_id = wp_posts.id
WHERE wp_posts.post_type = 'Product'
AND wp_postmeta.meta_key = '_regular_price'
GROUP BY wp_posts.post_title) AS x
INNER JOIN (SELECT wp_postmeta.post_id,
wp_posts.post_title,
wp_postmeta.meta_value
FROM wp_postmeta
JOIN wp_posts
ON wp_postmeta.post_id = wp_posts.id
WHERE wp_posts.post_type = 'Product'
AND wp_postmeta.meta_key = '_regular_price'
ORDER BY wp_posts.post_title,
wp_postmeta.meta_value) AS f
ON x.post_title = f.post_title
AND f.meta_value = x.minprice
GROUP BY f.post_title
)
);
START TRANSACTION;
DELETE FROM wp_posts
WHERE wp_posts.ID IN (SELECT ID FROM posts);
DELETE FROM wp_postmeta
WHERE wp_postmeta.post_id IN (SELECT post_id FROM postmeta);
COMMIT;
DELETE FROM posts;
DELETE FROM postmeta;
END;
Hi i have a problem to create an sql query request.
I have a working query to get published or private posts on category and a custom post-type and a meta_key 'mapcoordinates' in my wp_usermeta table on user_id.
What i try to accomplished is extending the query that I also get another meta_value from my wp_usermeta table with meta_key 'some_value'.
So I need to search on two meta_keys values (mapcoordinates , somevalue) instead off one (mapcoordinates), see working example
Maybe somebody can help me'
SELECT SQL_CALC_FOUND_ROWS wp_posts.ID, wp_usermeta.meta_value AS user_map_coord, wp_usermeta.user_id
FROM wp_posts
INNER JOIN wp_term_relationships ON (wp_posts.ID = wp_term_relationships.object_id)
INNER JOIN wp_usermeta ON ( wp_posts.post_author = wp_usermeta.user_id )
WHERE 1=1
AND ( wp_term_relationships.term_taxonomy_id IN (5,6))
AND wp_posts.post_type = 'ouder_type'
AND wp_usermeta.meta_key= 'mapcoordinates'
AND (wp_posts.post_status = 'publish' OR wp_posts.post_status = 'private')
GROUP BY wp_posts.ID ORDER BY wp_posts.post_date
Above query is working.
I like to extend it in something like this.
SELECT SQL_CALC_FOUND_ROWS wp_posts.ID, wp_usermeta.meta_value AS user_map_coord, wp_usermeta.user_id
FROM wp_posts
INNER JOIN wp_term_relationships ON (wp_posts.ID = wp_term_relationships.object_id)
INNER JOIN wp_usermeta ON ( wp_posts.post_author = wp_usermeta.user_id )
WHERE 1=1
AND ( wp_term_relationships.term_taxonomy_id IN ( 5,6))
AND wp_posts.post_type = 'ouder_type'
AND wp_usermeta.meta_key= 'mapcoordinates'
// this line added not working i get only one result , expecting 2 results
AND wp_usermeta.meta_key= 'somevalue'
AND (wp_posts.post_status = 'publish' OR wp_posts.post_status = 'private')
GROUP BY wp_posts.ID ORDER BY wp_posts.post_date
You have wp_usermeta.meta_key equal to two different things at the same time, so probably shouldn't get any results. I guess what you're after is more along the lines of:
AND wp_usermeta.meta_key IN ('mapcoordinates','somevalue')
I'm using this sql to return results based on an inner join with 3 meta values. It only seems to work with 1 AND ( ), when i add the other two it returns 0 results.
SELECT * FROM wp_posts
INNER JOIN wp_postmeta
ON ( wp_posts.ID = wp_postmeta.post_id )
WHERE wp_posts.post_type = 'plot'
AND wp_posts.post_status = 'publish'
AND ( wp_postmeta.meta_key = 'plot_type' AND wp_postmeta.meta_value = 'Cottage' )
AND ( wp_postmeta.meta_key = 'number_of_bedrooms' AND wp_postmeta.meta_value = '2' )
AND ( wp_postmeta.meta_key = 'property' AND wp_postmeta.meta_value = '446' )
GROUP BY wp_posts.ID
ORDER BY wp_posts.post_title ASC;
I think you meant to use OR with the other 2 (see below). The same field can't be 2 different things, which is why you get 0 results.
SELECT *
FROM wp_posts
INNER JOIN wp_postmeta
ON (wp_posts.ID = wp_postmeta.post_id)
WHERE wp_posts.post_type = 'plot'
AND wp_posts.post_status = 'publish'
AND ((wp_postmeta.meta_key = 'plot_type' AND
wp_postmeta.meta_value = 'Cottage') OR
(wp_postmeta.meta_key = 'number_of_bedrooms' AND
wp_postmeta.meta_value = '2') OR (wp_postmeta.meta_key = 'property' AND
wp_postmeta.meta_value = '446'))
GROUP BY wp_posts.ID
ORDER BY wp_posts.post_title ASC;
edit, try below instead:
select * from wp_posts
join wp_postmeta on wp_posts.ID = wp_postmeta.post_id
where wp_posts.post_type = 'plot'
and wp_posts.post_status = 'publish'
and concat(wp_postmeta.meta_key,'|',wp_postmeta.meta_value)
in ('plot_type|Cottage',
'number_of_bedrooms|2',
'property|446');
You need to join the wp_postmeta table once for each type of value you need.
SELECT whatever, whatever
FROM wp_posts AS p
JOIN wp_postmeta AS plottype
ON (p.ID = plottype.post_id AND plottype.meta_key = 'plot_type')
JOIN wp_postmeta AS bedrooms
ON (p.ID = bedrooms.post_id AND bedrooms.meta_key = 'number_of_bedrooms')
JOIN wp_postmeta AS property
ON (p.ID = property.post_id AND property.meta_key = 'property')
WHERE wp_posts.post_type = 'plot'
AND wp_posts.post_status = 'publish'
AND plottype.meta_value = 'Cottage'
AND bedrooms.meta_value = '2'
AND property.meta_value = '466'
GROUP BY wp_posts.ID
ORDER BY wp_posts.post_title ASC;
This wp_postmeta key/value storage is a little tricky to join to; your join criteria need to pull the appropriate key as well as the matching post ID.
It's well known that SELECT * is a bad idea in software. It's especially bad when you're joining so many tables. List the columns you need in your result set.
Notice also that you're using INNER JOIN with which JOIN is synonymous. If any of the values you're pulling from the metadata are missing, so will be the row from your result set. You may or may not be better off using LEFT JOINs (You didn't explain the purpose of the query.)
It seems as some of the structure for your conditions should be changed.
Try the following:
SELECT * FROM wp_posts
INNER JOIN wp_postmeta
ON ( wp_posts.ID = wp_postmeta.post_id )
WHERE wp_posts.post_type = 'plot'
AND wp_posts.post_status = 'publish'
AND (
(wp_postmeta.meta_key = 'plot_type' AND wp_postmeta.meta_value = 'Cottage')
OR
(wp_postmeta.meta_key = 'number_of_bedrooms' AND wp_postmeta.meta_value = '2')
OR
(wp_postmeta.meta_key = 'property' AND wp_postmeta.meta_value = '446')
)
ORDER BY wp_posts.post_title ASC;
I managed to fix the issue using WP_Meta_Query, the SQL it produced was...
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)
INNER JOIN wp_postmeta AS mt2 ON (wp_posts.ID = mt2.post_id)
WHERE 1=1
AND wp_posts.post_type = 'plot'
AND (wp_posts.post_status = 'publish')
AND (
(wp_postmeta.meta_key = 'property' AND CAST(wp_postmeta.meta_value AS CHAR) = '180')
AND (mt1.meta_key = 'plot_type' AND CAST(mt1.meta_value AS CHAR) = 'Cottage')
AND (mt2.meta_key = 'number_of_bedrooms' AND CAST(mt2.meta_value AS CHAR) = '2')
)
GROUP BY wp_posts.ID
ORDER BY wp_posts.post_title ASC;
Thanks to everyone for the help :)
I'm still pretty new to SQL, and I'm having trouble wrapping my mind around why one of these queries functions properly and one does not. This is stemming from my attempts to optimize a complex and slow query. #kalengi suggested what looks like a brilliant solution to me, but it doesn't seem to work on my site. Here are the queries.
This is the standard SQL that WordPress generates (this is working as expected):
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)
WHERE 1=1
AND wp_posts.post_type = 'product'
AND (wp_posts.post_status = 'publish')
AND (
(wp_postmeta.meta_key = '_visibility' AND CAST(wp_postmeta.meta_value AS CHAR) IN ('visible','catalog'))
AND (mt1.meta_key = '_stock_status' AND CAST(mt1.meta_value AS CHAR) = 'instock')
)
GROUP BY wp_posts.ID
ORDER BY wp_posts.menu_order,wp_posts.post_title asc
LIMIT 0, 10
This is the SQL after #kalengi's filter processes it to combine the multiple INNER JOINs into one (this returns 0 results):
SELECT SQL_CALC_FOUND_ROWS wp_posts.ID
FROM wp_posts
INNER JOIN wp_postmeta AS pmta ON (wp_posts.ID = pmta.post_id)
WHERE 1=1
AND wp_posts.post_type = 'product'
AND ( wp_posts.post_status = 'publish' )
AND (
( pmta.meta_key = '_visibility' AND CAST(pmta.meta_value AS CHAR) IN ( 'visible','catalog' ) )
AND ( pmta.meta_key = '_stock_status' AND CAST(pmta.meta_value AS CHAR) = 'instock' )
)
GROUP BY wp_posts.ID
ORDER BY wp_posts.menu_order,wp_posts.post_title asc
LIMIT 0, 10
Can anyone explain whey the second one doesn't work to me?
The two inner joins:
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)
represent two data sets (that happen to be "the same").
Given the WHERE condition, the results will be sets of two rows where the first (wp_postmeta) meets one condition:
(wp_postmeta.meta_key = '_visibility' AND CAST(wp_postmeta.meta_value AS CHAR) IN ('visible','catalog'))
And the second meets a completely different condition:
(mt1.meta_key = '_stock_status' AND CAST(mt1.meta_value AS CHAR) = 'instock')
By combining everything into one INNER JOIN, you're instead looking for ONE ROW which matches both conditions. Apparently there isn't one.
If you review the "brilliant solution", you'll see that 'AND's were changed to 'OR' to preserve the semantics:
AND (
( pmta.meta_key = '_visibility' AND CAST(pmta.meta_value AS CHAR) IN ( 'visible','catalog' ) )
OR ( pmta.meta_key = '_stock_status' AND CAST(pmta.meta_value AS CHAR) = 'instock' )
)
It looks like you have a condition in your WHERE clause that is looking for two values at the same time in the pmta.meta_key and pmta.meta_value columns:
....
( pmta.meta_key = '_visibility' AND CAST(pmta.meta_value AS CHAR) IN ('visible','catalog' ) )
AND ( pmta.meta_key = '_stock_status' AND CAST(pmta.meta_value AS CHAR) = 'instock' )
....
One column cannot have two different values in the same row, so this test will return FALSE, and consequently, no rows will be returned.
If you rewrite the original query to group the join conditions into the ON clauses, you can see why your second query won't work:
SELECT
SQL_CALC_FOUND_ROWS wp_posts.ID
FROM
wp_posts
INNER JOIN wp_postmeta
ON wp_posts.ID = wp_postmeta.post_id
AND wp_postmeta.meta_key = '_visibility'
AND CAST(wp_postmeta.meta_value AS CHAR) IN ('visible','catalog')
INNER JOIN wp_postmeta AS mt1
ON wp_posts.ID = mt1.post_id
AND mt1.meta_key = '_stock_status'
AND CAST(mt1.meta_value AS CHAR) = 'instock'
WHERE
1=1
AND wp_posts.post_type = 'product'
AND wp_posts.post_status = 'publish'
GROUP BY
wp_posts.ID
ORDER BY
wp_posts.menu_order,
wp_posts.post_title asc
LIMIT 0, 10
If you want to join the table only once, try something like this:
SELECT
SQL_CALC_FOUND_ROWS wp_posts.ID
FROM
wp_posts
INNER JOIN wp_postmeta
ON wp_posts.ID = wp_postmeta.post_id
AND (
wp_postmeta.meta_key = '_visibility'
AND CAST(wp_postmeta.meta_value AS CHAR) IN ('visible','catalog')
) OR (
wp_postmeta.meta_key = '_stock_status'
AND CAST(wp_postmeta.meta_value AS CHAR) = 'instock'
)
WHERE
1=1
AND wp_posts.post_type = 'product'
AND wp_posts.post_status = 'publish'
GROUP BY
wp_posts.ID
ORDER BY
wp_posts.menu_order,
wp_posts.post_title asc
LIMIT 0, 10
First I would like to mention I have limited skills when it comes to MYSQL an JOIN. However this is what I have and what I like to achieve:
I have the default WordPress tables and like to get a result with post_name, title, status and the meta_value from a certain meta key.
This is what I have:
SELECT
wp_posts.ID, wp_posts.post_name, wp_posts.post_title, wp_posts.post_status, wp_postmeta.meta_value
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 (
wp_term_relationships.term_taxonomy_id
IN ( 1, 2, 3 )
)
AND wp_posts.post_type = 'my_post_type'
AND (
wp_posts.post_status
IN (
'my_status_1', 'my_status_2'
)
)
AND wp_postmeta.meta_key = 'my_meta_key'
GROUP BY wp_posts.ID
ORDER BY wp_posts.ID ASC
Everything works as expected when each post has a postmeta of 'my_meta_key'. But if the 'my_meta_key' is missing the post is not in the result.
I guess it' caused by the second INNER JOIN but as mentioned I have no idea what I should it replace it with.
I'm sure it's something simple
Move the AND wp_postmeta.meta_key = 'my_meta_key' to
INNER JOIN
wp_postmeta ON ( wp_posts.ID = wp_postmeta.post_id )
Like this and change the INNER for a LEFT
LEFT JOIN
wp_postmeta ON ( wp_posts.ID = wp_postmeta.post_id
AND wp_postmeta.meta_key = 'my_meta_key')
If you put your condition in the "Where Clause" The Left join will be "overwritten".
And I will add an advice. Dont use parenthesis when you dont really need it to keep your code easy to read.
The following returns
all records from WP_Posts
only those records with WP_TERM_RELATIONSHIPS
only those records in wp_postmeta which a matching record in wp_posts.
*
SELECT wp_posts.ID, wp_posts.post_name, wp_posts.post_title,
wp_posts.post_status, wp_postmeta.meta_value
FROM wp_posts
LEFT JOIN wp_term_relationships
ON wp_posts.ID = wp_term_relationships.object_id
LEFT JOIN wp_postmeta
ON wp_posts.ID = wp_postmeta.post_id
WHERE wp_term_relationships.term_taxonomy_id IN ( 1, 2, 3 )
AND wp_posts.post_type = 'my_post_type'
AND wp_posts.post_statusIN ('my_status_1', 'my_status_2')
AND (wp_postmeta.meta_key = 'my_meta_key' or wp_postmeta.meta_key is null)
GROUP BY wp_posts.ID
ORDER BY wp_posts.ID ASC
you need the is null otherwise records in wp_posts without wp_postmeta data will be excluded.
Change the INNER JOIN on the wp_postmeta table to a LEFT JOIN:
LEFT JOIN wp_postmeta
ON ( wp_posts.ID = wp_postmeta.post_id )
And move the WHERE filter for the wp_postmeta to the JOIN condition.
So your query will be:
SELECT wp_posts.ID, wp_posts.post_name, wp_posts.post_title, wp_posts.post_status, wp_postmeta.meta_value
FROM wp_posts
INNER JOIN wp_term_relationships
ON ( wp_posts.ID = wp_term_relationships.object_id )
LEFT JOIN wp_postmeta
ON ( wp_posts.ID = wp_postmeta.post_id )
AND wp_postmeta.meta_key = 'my_meta_key'
WHERE wp_term_relationships.term_taxonomy_id IN ( 1, 2, 3 )
AND wp_posts.post_type = 'my_post_type'
AND wp_posts.post_status IN ('my_status_1', 'my_status_2')
GROUP BY wp_posts.ID
ORDER BY wp_posts.ID ASC
The INNER JOIN syntax will only return rows that match in both tables. So if you do not have a matching row, you will not get any result. By changing that to a LEFT JOIN, you will return all rows even if there is not a matching row in the wp_postmeta table. If the row does not exist, then the values from the wp_postmeta table will be null.