I have 2 tables - posts and postmeta.
posts
ID title category post_status post_type
1 ABC cat-1 Publish Store
2 DEF cat-2 Publish Store
3 GHI cat-3 Publish Store
4 JKL cat-2 Publish Store
5 XYZ cat-5 Draft Store
6 MNO cat-9 Publish Article
and
postmeta
meta_id post_id meta_key meta_value
109 1 city 1
110 1 featured h
111 2 city 1,2
112 2 featured both
113 3 city 2,3
114 3 featured both
115 4 city 1
116 4 featured n
117 5 city 1,4
118 5 featured h
119 6 city 1
120 6 featured h
I am trying to run a query that would give me a list of posts which has the following conditions:
Whose value against city has 1 AND
whose value for featured is either h or both AND
whose post status is Publish AND
whose post type is Store
Order them by title
The query I am trying is
SELECT DISTINCT posts.ID , posts.*, postmeta.*
FROM posts, postmeta
WHERE posts.ID = postmeta.post_id
AND (postmeta.meta_value = 'h' OR postmeta.meta_value = 'both')
AND (postmeta.meta_key = 'post_city_id' AND (postmeta.meta_value LIKE '%,1,%' OR postmeta.meta_value LIKE '%1,%' OR postmeta.meta_value LIKE '%,1%' OR postmeta.meta_value LIKE '%1%'))
AND posts.post_status = 'Publish'
AND posts.post_type = 'Store'
ORDER BY (SELECT postmeta.meta_value from postmeta where (posts.ID = postmeta.post_id) and postmeta.meta_key LIKE '%home_featured_type%') asc, posts.post_title LIMIT 0,6
The correct returns would be IDs 1 and 2 i.e. abc and def. But I am getting empty result. I cannot figure out where it is falling apart. How can this be fixed?
here is a fixed query, but I still don't understand the mysterious ORDER BY (SELECT) thingie.
http://sqlfiddle.com/#!2/5ce5a/19
SELECT DISTINCT posts.ID , posts.*, postmeta_city.*, postmeta_featured.*
FROM posts
INNER JOIN postmeta AS postmeta_city
ON postmeta_city.post_id = posts.ID
AND postmeta_city.meta_key = 'city'
AND ( postmeta_city.meta_value LIKE '%,1,%'
OR postmeta_city.meta_value LIKE '%1,%'
OR postmeta_city.meta_value LIKE '%,1%'
OR postmeta_city.meta_value LIKE '%1%'
)
INNER JOIN postmeta AS postmeta_featured
ON postmeta_featured.post_id = posts.ID
AND postmeta_featured.meta_key = 'featured'
AND ( postmeta_featured.meta_value = 'h'
OR postmeta_featured.meta_value = 'both'
)
WHERE posts.post_status = 'Publish'
AND posts.post_type = 'Store'
ORDER BY (
SELECT postmeta.meta_value
FROM postmeta
WHERE ( posts.ID = postmeta.post_id )
AND postmeta.meta_key LIKE '%home_featured_type%'
) asc,
posts.title
LIMIT 0,6;
;
Updated with other people's ideas, please upvote them:
http://sqlfiddle.com/#!2/5ce5a/33
SELECT DISTINCT posts.ID , posts.*, postmeta_city.*, postmeta_featured.*
FROM posts
INNER JOIN postmeta AS postmeta_city
ON postmeta_city.post_id = posts.ID
AND postmeta_city.meta_key = 'city'
AND FIND_IN_SET('1', postmeta_city.meta_value)
INNER JOIN postmeta AS postmeta_featured
ON postmeta_featured.post_id = posts.ID
AND postmeta_featured.meta_key = 'featured'
AND postmeta_featured.meta_value IN ('h','both')
WHERE posts.post_status = 'Publish'
AND posts.post_type = 'Store'
ORDER BY (
SELECT postmeta.meta_value
FROM postmeta
WHERE ( posts.ID = postmeta.post_id )
AND postmeta.meta_key LIKE '%home_featured_type%'
) asc,
posts.title
LIMIT 0,6;
;
You're getting an empty result set because you are ANDing the meta_value column so it has to equal two values at the same time which is impossible. Something like val = '1' AND val = 'both' will always return false and none of the rows would join. Instead, you must use an OR between conditions: city -> 1 and featured -> h/both.
Since the post must contain both city -> 1 and featured -> h/both (which are not across columns but across multiple rows), you'll need a HAVING clause in conjunction with a GROUP BY to ensure each post joins with two rows, satisfying both conditions... not one or the other.
Also, that is an awful lot of LIKE's to check for the existance of a 1. You can use FIND_IN_SET instead:
SELECT
*
FROM
posts a
INNER JOIN
postmeta b ON a.ID = b.post_id
AND
(
(b.meta_key = 'city' AND FIND_IN_SET('1', b.meta_value) > 0)
OR
(b.meta_key = 'featured' AND b.meta_value IN ('h', 'both'))
)
WHERE
a.post_status = 'Publish'
AND a.post_type = 'Store'
GROUP BY
a.ID
HAVING
COUNT(*) = 2
ORDER BY
a.title
Try this :
SELECT DISTINCT posts.ID , posts.*, postmeta.*
FROM posts AS p INNER JOIN postmeta AS pm
WHERE p.ID = pm.post_id
AND (pm.meta_value in ('h','both')
AND (pm.meta_key = 'city' AND
(pm.meta_value LIKE '%,1,%' OR pm.meta_value LIKE '%1,%' OR pm.meta_value LIKE '%,1%' OR pm.meta_value LIKE '%1%'))
AND posts.post_status = 'Publish'
AND posts.post_type = 'Store'
ORDER BY p.title LIMIT 0,6
Since you want order by title only, so there is no need to write any query in 'order by' clause.
Related
this is my query, apart Wordpress prefix, it's just a matter of MySQL.
Scroll to WHERE CLAUSE when you will find all the options.
basically give me back all the results wether $searchString is either in the post_title, OR in the postmeta field called “operations_long_bio" OR “operations_short_bio"
this is working quite as expected but I would also like to have in the results, in any case ALWAYS the content of the column whose meta_key value is “operations_short_bio"
SELECT
posts.post_title AS name, posts.ID as ID,
postmeta.meta_value AS description
FROM `{$wpdb->base_prefix}posts` AS posts
LEFT JOIN `{$wpdb->base_prefix}postmeta` AS postmeta
ON posts.ID=postmeta.post_id
WHERE
posts.post_type = 'post'
AND
posts.post_status = 'publish'
AND
(
(
postmeta.meta_key
IN('operations_long_bio', 'operations_short_bio')
AND postmeta.meta_value LIKE '%$searchStr%'
)
OR ( posts.post_title LIKE '%$searchStr%' )
)
GROUP BY posts.ID
");
I tried forcing:
SELECT
posts.post_title AS name, posts.ID as ID,
postmeta.meta_value AS description
/***********/
CASE postmeta.meta_key='operations_short_bio' THEN postmeta.meta_value AS short_description END
...
but throws an error.
don't really actually know how to ask "give me ALSO the field postmeta.meta_value AS short_description WHEN it's postmeta.meta_key='operations_short_bio' :(
any hint?
tyvm!!
The Syntax is correctly in this form
CASE WHEN postmeta.meta_key='operations_short_bio' THEN postmeta.meta_value END AS short_description
And here is an example from your query, iha to adept it a litte, as this is not php
CREATE TABLE posts (ID int,post_title varchar(100), post_type varchar(10),post_status varchar(10))
CREATE tABLE postmeta (post_id int,meta_key varchar(100),meta_value varchar(100))
SELECT CASE WHEN postmeta.meta_key='operations_short_bio' THEN postmeta.meta_value END AS short_description
FROM postmeta
| short_description |
| :---------------- |
SELECT
posts.post_title AS name, posts.ID as ID,
postmeta.meta_value AS description,
MAX(CASE WHEN postmeta.meta_key='operations_short_bio' THEN postmeta.meta_value END) AS short_description
FROM `posts` AS posts
LEFT JOIN `postmeta` AS postmeta
ON posts.ID=postmeta.post_id
WHERE
posts.post_type = 'post'
AND
posts.post_status = 'publish'
AND
(
(
postmeta.meta_key
IN('operations_long_bio', 'operations_short_bio')
AND postmeta.meta_value LIKE '%$searchStr%'
)
OR ( posts.post_title LIKE '%$searchStr%' )
)
GROUP BY posts.ID,posts.post_title,postmeta.meta_value
name | ID | description | short_description
:--- | -: | :---------- | :----------------
db<>fiddle here
I have the following query that bring the posts that contain a specific word in the title
SELECT posts_post.ID AS post_ID,
posts_post.post_date AS post_post_date,
CONCAT('',posts_post.post_title,'') AS post_title_with_link_to_post
FROM wp_posts AS posts_post
WHERE 1=1
AND posts_post.post_title LIKE '%HOTARAR%'
AND posts_post.post_type = 'post'
GROUP BY post_post_date
The problem now is that I need to bring the posts only from a specific category (tag slug for the category is hotarari-consiliu-local and has the ID 160), how could I modify the above query to bring posts only from a single blog posts category? Thanks!
Try the below query.
global $wpdb;
$make = $wpdb->get_results("
SELECT * FROM
$wpdb->posts
LEFT JOIN
$wpdb->term_relationships
ON
($wpdb->posts.ID = $wpdb->term_relationships.object_id)
LEFT JOIN
$wpdb->term_taxonomy
ON
($wpdb->term_relationships.term_taxonomy_id = $wpdb->term_taxonomy.term_taxonomy_id)
WHERE
$wpdb->posts.post_type = 'post'
AND
$wpdb->posts.post_title LIKE '%HOTARAR%'
AND
$wpdb->term_taxonomy.taxonomy = 'category'
AND
$wpdb->term_taxonomy.term_id = 160
ORDER BY
post_date DESC
");
Assuming Bhautik is on the right path, then something like this should work (I don't know where 'hotarari-consiliu-local' belongs in this).
SELECT p.ID post_ID
, p.post_date post_post_date
, CONCAT('',p.post_title,'') post_title_with_link_to_post
FROM wp_posts p
JOIN wp_term_relationships pt
ON pt.object_id = p.ID
JOIN wp_term_taxonomy t
ON t.term_taxonomy_id = pt.term_taxonomy_id
WHERE p.post_type = 'post'
AND p.post_title LIKE '%HOTARAR%'
AND t.taxonomy = 'category'
AND t.term_id = 160
ORDER
BY post_date DESC
I am writing a basic CMS system with NodeJS and MySQL. I am using the tables structure for posts that WordPress also uses, that being 'posts' and 'post_meta'.
The 'posts' table has the following columns...
id
post_title
post_date
...etc
The 'post_meta' table has the following columns...
id
post_id - ID of associated post
meta_key
meta_value
I like this style as it keeps my rows easy to read in a SQL editor without having to scroll as long lengths, as well as add additional meta data as needed.
Using JOINs (Query below) I am able to make a result set that contains the values from 'posts', as well as additional columns named after 'post_meta.meta_key' that contain the value of 'post_meta.meta_value'.
The problem I am currently running into is how I would go about returning results where a 'meta_key' is not present in the 'posts_meta' table, and instead filling that column of the result with NULL.
Example usage. 2 Posts are in the 'posts' table, the first has 2 meta_keys associated with it, the other has only 1.
'posts' table contents
id : 1
title : 'Post 1'
post_date : 'timestamp of posted date/time'
id : 2
title : 'Post 2'
post_date : 'timestamp of posted date/time'
'posts_meta' table contents
id : 1
post_id : 1
meta_key : 'key1'
meta_value : 'key1 value'
id : 2
post_id : 1
meta_key : 'key2'
meta_value : 'key2 value'
id : 3
post_id : 2
meta_key : 'key1'
posts_meta.meta_value : 'key1 value'
the results I would like to get from this are
post_id : 1
title : 'Post 1'
post_date : 'timestamp of posted date/time'
key1 : 'key1 value'
key2 : 'key2 value'
post_id : 2
title : 'Post 2'
post_date : 'timestamp of posted date/time'
key1 : 'key1 value'
key2 : NULL
However, with the code I am currently working with, the second result (Post 2) does not return as part of the results, only 'Post 1', since all of the meta_keys that were being tested for exist in the 'post_meta' table.
Here is my SQL code as it currently stands (edited to keep to the example above)
SELECT
posts.id,
posts.title,
posts.post_date,
m1.meta_value as `key1`,
m2.meta_value as `key2`
FROM posts
LEFT JOIN post_meta as m1 ON ( posts.id = m1.post_id )
LEFT JOIN post_meta as m2 ON ( posts.id = m2.post_id )
WHERE
( m1.meta_key = 'key1' )
AND ( m2.meta_key = 'key2' )
ORDER BY posts.post_date DESC
as you can see, i am selecting the meta_values from the 2 JOINs, and naming them after the specific key each of those joins is being used to retrieve (as defined in the WHERE clause).
I know it has got to be something simple, since I am almost there. It returns rows that have both key1 and key2, but not rows that are completely missing either or all of them. I want it to return all rows, and if a key is missing, fill that field in the row with NULL
You want FULL OUTER JOIN rather than LEFT JOIN.
Left Join will not include rows that do not exist in the other table. In order to have the data (even if it is NULL). You need to do a FULL OUTER JOIN, that will give you cells with NULL rather than leaving them off entirely.
Since you are using MySQL, you will have to emulate the behavior of a FULL OUTER JOIN using UNION. Something like this:
SELECT
posts.id,
posts.title,
posts.post_date,
m1.meta_value as `key1`,
m2.meta_value as `key2`
FROM posts
LEFT JOIN post_meta as m1 ON ( posts.id = m1.post_id )
LEFT JOIN post_meta as m2 ON ( posts.id = m2.post_id )
WHERE
( m1.meta_key = 'key1' )
AND ( m2.meta_key = 'key2' )
ORDER BY posts.post_date DESC
UNION
SELECT
posts.id,
posts.title,
posts.post_date,
m1.meta_value as `key1`,
m2.meta_value as `key2`
FROM posts
RIGHT JOIN post_meta as m1 ON ( posts.id = m1.post_id )
LEFT JOIN post_meta as m2 ON ( posts.id = m2.post_id )
WHERE
( m1.meta_key = 'key1' )
AND ( m2.meta_key = 'key2' )
ORDER BY posts.post_date DESC
UNION
SELECT
posts.id,
posts.title,
posts.post_date,
m1.meta_value as `key1`,
m2.meta_value as `key2`
FROM posts
RIGHT JOIN post_meta as m1 ON ( posts.id = m1.post_id )
RIGHT JOIN post_meta as m2 ON ( posts.id = m2.post_id )
WHERE
( m1.meta_key = 'key1' )
AND ( m2.meta_key = 'key2' )
ORDER BY posts.post_date DESC
I have currently have a problem with searching products with tags:
Products
id name
1 lightbulb
Tags
id name
1 energy
2 light
3 lights
Tagships
id taggable_id tag_id
1 1 1
1 1 2
1 1 3
I need to build a query to get the products that are tagged as (energy) and (light or lights)
So far this doesn't work:
SELECT ..<snipped>..
FROM `products`
LEFT OUTER JOIN `tagships`
ON (`products`.`id` = `tagships`.`taggable_id`)
LEFT OUTER JOIN `tags`
ON (`tags`.`id` = `tagships`.`tag_id`)
WHERE ((tags.name = 'energy' OR tags.name = 'energies')
AND (tags.name = 'light' OR tags.name = 'lights'))
GROUP BY products.id
HAVING COUNT(tagships.tag_id) <= 2
ORDER BY products.updated_at DESC
UPDATED the query.
Note: I need the query to search for singular and plural tense of the tag as seen in the above query.
try the following query:
SELECT ..<snipped>..
FROM `products`
LEFT OUTER JOIN `tagships`
ON (`products`.`id` = `tagships`.`taggable_id`
AND `tagships`.`taggable_type` = 'Product')
LEFT OUTER JOIN `tags`
ON (`tags`.`id` = `tagships`.`tag_id`)
WHERE ((tags.name = 'energy' OR tags.name = 'energies')
OR (tags.name = 'light' OR tags.name = 'lights'))
GROUP BY products.id
HAVING COUNT(tagships.tag_id) <= 2
ORDER BY products.updated_at DESC
Apparently, a tag can't be 'energy' or 'energies' and, at the same time, 'light' or 'lights'. So, the condition in your query must consist of ORs only:
WHERE t.name = 'energy'
OR t.name = 'energies'
OR t.name = 'light'
OR t.name = 'lights'
Alternatively, of course, you can use IN:
WHERE t.name IN ('energy', 'energies', 'light', 'lights')
Another thing is, how to check that a product has both at least one of the first two and at least one of the other two. I would probably do it using a HAVING clause like this:
HAVING COUNT(CASE WHEN t.name IN ('energy', 'energies') THEN 1 END) > 0
AND COUNT(CASE WHEN t.name IN ('light', 'lights') THEN 1 END) > 0
This works:
SELECT distinct p.id, p.name
FROM products as p
join tagships as ts on ts.product_id = p.id
join tags as t on t.id = ts.tag_id
where t.name in ('energy', 'energies', 'light', 'lights');
I've used aliases to make the code more readable. Also you don't want outer joins, since you don't want records that don't match in the joined tables.
I'm having some difficulty with my SQL statement. I'm doing a query on WordPress to display posts based on multiple post meta fields. When I do the query and filter with only one meta field, or a OR on multiple it works, however AND on multiple fails.
SELECT wposts . *
FROM wp_posts wposts
INNER JOIN (
SELECT post_id
FROM wp_postmeta wpostmeta
WHERE (
(wpostmeta.meta_key = 'ulnooweg_business_industry'
AND wpostmeta.meta_value = 'Legal Services')
AND (
wpostmeta.meta_key = 'ulnooweg_business_province'
AND wpostmeta.meta_value = 'New Brunswick')
)
GROUP BY post_id
)
AS t ON t.post_id = wposts.ID
WHERE wposts.post_status = 'publish'
AND wposts.post_type = 'business'
ORDER BY wposts.post_title ASC
LIMIT 0 , 30
Your query is testing if meta_key (and meta_value) is 2 different values in the same row, which is impossible. But I see what you are trying to do..
Try joining the wp_postmeta table twice except each with an ON clause that excludes all rows except those that satisfy the meta_key condition:
SELECT
p.*,
GROUP_CONCAT(CONCAT(pm.meta_key,':',pm.meta_value) SEPARATOR ',') AS meta_values
FROM
wp_posts p
JOIN wp_postmeta pm ON pm.post_id = p.ID
JOIN wp_postmeta pm_bi ON (pm_bi.post_id = p.ID AND pm_bi.meta_key = 'ulnooweg_business_industry')
JOIN wp_postmeta pm_bp ON (pm_bp.post_id = p.ID AND pm_bp.meta_key = 'ulnooweg_business_province')
WHERE
pm_bi.meta_value = 'Legal Services'
AND pm_bp.meta_value = 'New Brunswick'
AND p.post_type = 'business'
AND p.post_status = 'publish'
GROUP BY p.ID
ORDER BY p.post_title ASC
Note: I joined the wp_postmeta table 3 times here to help prove that the conditions are satisfied, but you can remove the GROUP_CONCAT line (and the comma on the previous line of course) and the first JOIN to wp_postmeta and the query will work the same.
In the subquery, it looks like it's looking for records where both wpostmeta.meta_key = 'ulnooweg_business_industry' and wpostmeta.meta_key = 'ulnooweg_business_province' -- in other words, wpostmeta.meta_key needs to be equal to two strings simultaneously to satisfy this condition. Also, it's looking for wpostmeta.meta_value = 'Legal Services' and wpostmeta.meta_value = 'New Brunswick'.
My guess is that this is what you want in the WHERE clause of the subquery -- change one of the ANDs to an OR:
....
WHERE (
(wpostmeta.meta_key = 'ulnooweg_business_industry'
AND wpostmeta.meta_value = 'Legal Services')
OR ( -- changed to an OR
wpostmeta.meta_key = 'ulnooweg_business_province'
AND wpostmeta.meta_value = 'New Brunswick')
)
....
The problem is in the where clause of your inner select; I'm guessing wpostmeta returns MULTIPLE rows. A previous comment that a string can't be two values is correct. The 2nd approach should work if the 1st doesn't
at first I thought
WHERE
((wpostmeta.meta_key = 'ulnooweg_business_industry' AND wpostmeta.meta_value = 'Legal Services') OR
(wpostmeta.meta_key = 'ulnooweg_business_province' AND wpostmeta.meta_value = 'New Brunswick'))
Group by Post_ID
HAVING count(post_ID) = 2
This will work ONLY if there is only one record in the wpostmeta for each type of entry. If
postmeta.meta_key = 'ulnooweg_business_industry' AND wpostmeta.meta_value = 'Legal Services' can occur twice, then the above does't work.
2nd approach
Select wposts.*
FROM WP_Posts wposts
INNER JOIN (
Select POST_ID from WP_POSTMeta where meta_key = 'ulnooweg_business_industry' AND wpostmeta.meta_value = 'Legal Services'
INTERSECT
SELECT POST_ID FROM WP_POST_META WHERE meta_key = 'ulnooweg_business_province' AND wpostmeta.meta_value = 'New Brunswick'
)
AS T on T.Post_ID = wposts.ID