Advanced MySQL query in Wordpress with JOIN - mysql

I am new to MySQL and I am trying to write a quite advanced query. But hey! Learning by doing & Stackoverflow!
I could probably get all data in a less advanced query/queries and sort the data using PHP. But I figure it could be done directly in the query.
Below is my code. Please ask question if you don't understand and I will try to explain better. And please help me correct the code if you find any errors.
The code will be used to display different fields on my wordpress pages. Different fildes will have different categories eg. sidebar-blog, sidebar-page, highlight-blog, highlight-page. It will work almost like regular posts.
Here is the database structure of Wordpress:
http://codex.wordpress.org/images/9/9e/WP3.0-ERD.png
http://codex.wordpress.org/Database_Description
Questions:
How am I supose to join the tables: wp_posts, wp_term_relationships, wp_term_taxonomy, wp_terms AND wp_postmeta?
Is it good practice to make advanced queries or should you use PHP to handle if/else functions?
<?php
$id = $post->ID; // Gets the ID of current page
$query = "
SELECT wp_posts.post_content, wp_posts.ID, wp_terms.slug # Data from two different tables
FROM wp_posts
# Cant figure out how to join the tables
INNER JOIN wp_postmeta
ON wp_posts.ID = wp_postmeta.post_id
INNER JOIN wp_term_relationships
ON wp_posts.ID = wp_term_relationships.object_id
INNER JOIN wp_term_taxonomy
ON wp_term_relationships.term_taxonomy_id = wp_term_taxonomy.term_taxonomy_id
INNER JOIN wp_terms
ON wp_term_taxonomy.term_id = wp_terms.term_id
WHERE
wp_posts.post_type = 'my-own-post-type' # Only get specific post-type
AND
wp_posts.post_status = 'publish' # Only get published posts
AND
# START - Only get categories specified here
(
wp_terms.slug = 'category-1'
OR
wp_terms.slug = 'category-2'
OR
wp_terms.slug = 'category-3'
OR
wp_terms.slug = 'category-4'
OR
wp_terms.slug = 'category-5'
)
# END - Only get categories specified here
AND
# I want to be able to include or exclude specific pages, even if category is the right one
# Exlude = Don't get data if current page ID is found (ID of current page will be checked in meta_value using %,$id,%)
# Include means: If include value is specifyed, then get data ONLY if ID is found (ID of current page will be checked in meta_value using %,$id,%)
# If not exclude and include are set, it should get the data
# START - Include exclude
(
# exclude is set, so check if page id match anywhere. If it IS found it it should not get the data, otherwise YES
wp_postmeta.meta_key = 'exclude' AND wp_postmeta.meta_value <> '%,$id,%'
OR
# include is set, so check if page id match anywhere. If it IS NOT found it it should not get the data, otherwise YES
wp_postmeta.meta_key = 'include' AND wp_postmeta.meta_value = '%,$id,%'
OR
# If exclude and include aren't set it should get the data
wp_postmeta.meta_key <> 'exkludera' AND wp_postmeta.meta_key <> 'include'
)
# END - Include exclude
";
$result = mysql_query($query);
while($row = mysql_fetch_array($result))
{
// collect all fields in category 1 (eg. sidebar)
if($row['slug'] == 'category-1'){
$all_fields_from_category_1 = $all_fields_from_category_1.'
<div id="field-sidebar">
'.$row['post_content'].'
</div>
';}
// collect all fields in category 1 (eg. highlight)
if($row['slug'] == 'category-2'){
$all_fields_from_category_2 = $all_fields_from_category_2.'
<div id="field-highlight">
'.$row['post_content'].'
</div>
';}
} // end while
// sidebar
echo '<div id="container-sidebar">'.
$all_fields_from_category_1.
'</div>';
// highlight
echo '<div id="container-highlight">'.
$all_fields_from_category_1.
'</div>';
?>
New version, new question:
SELECT DISTINCT wp_posts.post_content, wp_posts.ID, wp_terms.slug
FROM wp_posts
JOIN wp_postmeta ON wp_posts.ID = wp_postmeta.post_id
JOIN wp_term_relationships ON wp_posts.ID = wp_term_relationships.object_id
JOIN wp_term_taxonomy ON wp_term_relationships.term_taxonomy_id = wp_term_taxonomy.term_taxonomy_id
JOIN wp_terms ON wp_term_taxonomy.term_id = wp_terms.term_id
WHERE wp_posts.post_type = 'my-own-fields'
AND wp_posts.post_status = 'publish'
AND wp_terms.slug IN
('field1', 'field2', 'field3', 'field4', 'field5', 'field6')
AND
(
wp_postmeta.meta_key = 'exlude' AND wp_postmeta.meta_value <> '$id'
OR wp_postmeta.meta_key = 'include' AND wp_postmeta.meta_value = '$id'
OR wp_postmeta.meta_key <> 'exlude' AND wp_postmeta.meta_value <> 'include' #LAST ROW
)
The "#LAST ROW" gives me back data even if exclude or include is set. Thats becauce it matches two other rows in the table wp_postmeta.
Example of table wp_postmeta:
meta_id post_id meta_key meta_value
1 30 include 18
2 30 _edit_lock 1322225789:1
3 30 _edit_last 1
If meta_key inklude or exclude cant be found for the post_id I want to return the data...
Example senario when I want to return the data:
meta_id post_id meta_key meta_value
2 30 _edit_lock 1322225789:1
3 30 _edit_last 1
Any idea of how I can solve this?
A statement that say: If no rows with exclude or include was found for current id. Return the data.
More examples:
The post_id is 30.
If senario is:
meta_id post_id meta_key meta_value
1 30 include 18
2 30 _edit_lock 1322225789:1
3 30 _edit_last 1
Then I want to get back wp_posts.post_content, wp_posts.ID and wp_terms.slug AS LONG AS $id IS 18.
If senario is:
meta_id post_id meta_key meta_value
1 30 exclude 18
2 30 _edit_lock 1322225789:1
3 30 _edit_last 1
Then I want to get back wp_posts.post_content, wp_posts.ID and wp_terms.slug AS LONG AS $id IS NOT 18.
If senario is:
meta_id post_id meta_key meta_value
2 30 _edit_lock 1322225789:1
3 30 _edit_last 1
Then I want to get back wp_posts.post_content, wp_posts.ID and wp_terms.slug.

Your joins are done correctly. When removing your comments and extra lines, you will see that the query is not really that complicated. The only real change I made was to change your list of OR for wp_terms.slug to an IN statement. This was only done to reduce code duplication, clean up the look, not to change functionality.
SELECT p.post_content, p.ID, t.slug
FROM wp_posts AS p
INNER JOIN wp_postmeta AS pm ON p.ID = pm.post_id
INNER JOIN wp_term_relationships AS tr ON p.ID = tr.object_id
INNER JOIN wp_term_taxonomy AS tt ON tr.term_taxonomy_id = tt.term_taxonomy_id
INNER JOIN wp_terms AS t ON tt.term_id = t.term_id
WHERE p.post_type = 'my-own-post-type'
AND p.post_status = 'publish'
AND t.slug IN
('category-1', 'category-2', 'category-3', 'category-4', 'category-5')
AND
(
( pm.meta_key = 'exclude' AND pm.meta_value <> '$id' )
OR ( pm.meta_key = 'include' AND pm.meta_value = '$id' )
OR ( pm.meta_key <> 'exclude' AND pm.meta_value <> 'include' )
)

Related

Multiple Meta_Key Select on on Wordpress database

I've developed a query which selects events from Wordpress. I am using the where clause to select where the meta_value of the meta_key eventstartdate is after today.
The issue I'm having now is, I also want filter on a second meta_value that being from the meta_key '_VenueCity'.
I have tried aliasing the wp_postmeta table and doing a where on the meta_key but I think I'm missing a join.
This is the code that works without my additional code to get it to work. Can any one advise on how I get this to work?
SELECT
`wp_posts`.`ID` AS `EventID`,
`wp_posts`.`post_parent` AS `SeriesID`,
`wp_posts`.`post_title` AS `EventTitle`,
`wp_posts`.`post_content` AS `EventDescription`,
`wp_posts`.`post_excerpt` AS `EventSummary`,
`wp_posts`.`post_name` AS `EventSlug`,
min(`wp_postmeta`.`meta_value`) AS `EventStartDate`,
max(`tribe_event_end_date`.`meta_value`) AS `EventEndDate`,
`wp_posts`.`guid` AS `GUID`
FROM ((`wp_posts`
JOIN `wp_postmeta` ON
(
(`wp_posts`.`ID` = `wp_postmeta`.`post_id`)
))
LEFT JOIN `wp_postmeta` `tribe_event_end_date` ON
(
(
(`wp_posts`.`ID` = `tribe_event_end_date`.`post_id`) AND
(`tribe_event_end_date`.`meta_key` = '_EventEndDate')
)
))
WHERE
(
(`wp_postmeta`.`meta_key` = '_EventStartDate') AND
(`wp_posts`.`post_type` = 'tribe_events') AND
(`wp_posts`.`post_status` = 'publish') AND
(`tribe_event_end_date`.`meta_value` >= CURDATE())
)
GROUP BY
`wp_posts`.`ID`
ORDER BY
`EventStartDate`,
`wp_posts`.`post_date`;
I am not going to write your query for you but I will give an example of how to get multiple postmeta values. The power will be in the where clause to get the right values.
You should also consider what joins you want to use.
SELECT
p.post_title,
pm1.meta_value,
pm2.meta_value
FROM wp_posts as p
INNER JOIN wp_postmeta as pm1
ON p.ID = pm1.post_id
INNER JOIN wp_postmeta as pm2
ON p.ID = pm2.post_id
WHERE
pm1.meta_key = '_my_postmeta_field1'
AND
pm2.meta_key <> '_not_this_field'

How to get Wordpress post with title, content and featured image using mysql query

Hi I know this question may seems familiar but please read the full question.
I want to get Wordpress post title,featured image,content using a mysql query.
I tried many queries but I'm getting errors.
there is no error and MySQL query returns empty. I am using Wordpress version 4.5.2.
I found this query but it is giving me an empty result.
SELECT p1.*, wm2.meta_value
FROM wp_posts p1
LEFT JOIN wp_postmeta wm1 ON (
wm1.post_id = p1.id
AND wm1.meta_value IS NOT NULL
AND wm1.meta_key = '_thumbnail_id'
)
LEFT JOIN
wp_postmeta wm2
ON (
wm1.meta_value = wm2.post_id
AND wm2.meta_key = '_wp_attached_file'
AND wm2.meta_value IS NOT NULL
)LEFT JOIN
wp_term_relationships wtr
ON
(
object_id=p1.id
)
WHERE
p1.post_status='publish'
AND p1.post_type='post'
AND 'term_taxonomy_id'='454'
ORDER BY p1.post_date DESC
LIMIT 0,10
How can I improve this query to return results?
You have an error in SQL's WHERE clause:
AND 'term_taxonomy_id'='454'
The string 'term_taxonomy_id' will never be equal to the string '454'.
First, to check, remove this condition entirely from your query. If you get results back and you still want to filter by this taxonomy_id then remove the single quote marks around the field name:
SELECT p1.*,
wm2.meta_value
FROM wp_posts p1
LEFT JOIN wp_postmeta wm1 ON (
wm1.post_id = p1.id
AND wm1.meta_value IS NOT NULL
AND wm1.meta_key = '_thumbnail_id'
)
LEFT JOIN wp_postmeta wm2 ON (
wm1.meta_value = wm2.post_id
AND wm2.meta_key = '_wp_attached_file'
AND wm2.meta_value IS NOT NULL
)
LEFT JOIN wp_term_relationships wtr ON (object_id = p1.id)
WHERE p1.post_status = 'publish'
AND p1.post_type = 'post'
AND term_taxonomy_id = '454'
ORDER BY p1.post_date DESC LIMIT 0,10
In MySQL you can use backticks around field names, but single quotes are used around string literals. If you mix those up, you'll be in trouble.
If you are still not getting results consider removing the p1.post_status and p1.post_type restrictions to see if you get results back and tweak accordingly.

Wordpress SQL query to fetch the meta_key names in a custom post type

Using WP dataTables plugin and my MySQL sucks so please bear with me.
I'm trying to fetch multiple associated meta_keys (custom field names) that are allocated to a custom post type in WordPress, and display the fields as the table header with the custom field values in the rows underneath the header.
My SQL below kinda works. While it pulls in a colum called 'meta_key' and 'meta_value', it has the actual names of the key as it's data. I want the meta_key value itself as the list returned.
SELECT * FROM wp_posts INNER JOIN wp_postmeta
ON wp_posts.ID = wp_postmeta.post_id
WHERE 1=1 AND wp_posts.post_type = 'my_custom_post'
AND (wp_posts.post_status = 'publish' OR wp_posts.post_status = 'private')
AND (wp_postmeta.meta_key = 'call_date' OR wp_postmeta.meta_key = 'call_time' OR wp_postmeta.meta_key = 'caller_number' OR wp_postmeta.meta_key = 'call_duration' OR wp_postmeta.meta_key = 'call_status' )
ORDER BY wp_posts.menu_order, wp_posts.post_date DESC LIMIT
So this creates a table with the header as 'meta_key' but I want the custom field name itself as the header. Hope this makes sense.
PS I've also tried
SELECT caller_number, call_duration, call_status, call_date, call_time
FROM wp_posts
WHERE post_type = 'my_custom_post'
AND post_status = 'publish'
FROM wp_postmeta
WHERE post_id = post_id
But it shows no data...
Seriously, any help here would be awesome.
for specific keys
get_post_meta($postid, '_metakey', single);
for all keys
get_post_meta($postid);
so you can do a foreach loop if you return just the custom posts in your sql query. But for posts, wp_query does a good job of returning posts rather than custom mysql queried.
See what is all returning...
global $wpdb;
global $post;
$cpt = "my_custom_post";
$sql = "SELECT * FROM {$wpdb->prefix}posts INNER JOIN {$wpdb->prefix}postmeta
ON {$wpdb->prefix}posts.ID = {$wpdb->prefix}postmeta.post_id
WHERE 1=1 AND {$wpdb->prefix}posts.post_type = '$cpt'
AND ({$wpdb->prefix}posts.post_status = 'publish' OR {$wpdb->prefix}posts.post_status = 'private')
AND ({$wpdb->prefix}postmeta.meta_key = 'call_date' OR {$wpdb->prefix}postmeta.meta_key = 'call_time' OR {$wpdb->prefix}postmeta.meta_key = 'caller_number' OR {$wpdb->prefix}postmeta.meta_key = 'call_duration' OR {$wpdb->prefix}postmeta.meta_key = 'call_status' )
ORDER BY {$wpdb->prefix}posts.menu_order, {$wpdb->prefix}posts.post_date DESC";
$data = $wpdb->get_results($sql);
echo "<pre>";
var_dump($data);
echo "</pre>";
die();

How do I perform a between Query on a DB that all ready has multiple joins?

I am trying to write a query to check if the custom meta values in a custom post type(properties) when saved equals the meta values in another custom post type(alerts)
I can get this work for normal match fields however I am trying to get it to work for a range for example, if 'bedrooms' in 'properties' is between 'a_bedrooms_min' and 'a_bedrooms_max' in 'alerts'. My Query looks like this so far:
//Property Meta Fields
$meta_type = 'flat';
$meta_bedrooms = '2';
//Alert Meta Fields
a_bedrooms_min = 1
a_bedrooms_max = 3
SELECT ID
FROM wp_posts
INNER JOIN wp_postmeta m1
ON ( wp_posts.ID = m1.post_id )
INNER JOIN wp_postmeta m2
ON ( wp_posts.ID = m2.post_id )
INNER JOIN wp_postmeta m3
ON ( wp_posts.ID = m3.post_id )
WHERE
wp_posts.post_type = 'alerts'
AND wp_posts.post_status = 'publish'
AND ( m1.meta_key = 'a_property_type' AND m1.meta_value = '$meta_type' )
AND ( m2.meta_key = 'a_bedrooms_min' AND m2.meta_value >= '$meta_bedrooms' )
AND ( m3.meta_key = 'a_bedrooms_max' AND m3.meta_value <= '$meta_bedrooms' )
GROUP BY wp_posts.ID
ORDER BY wp_posts.post_date
DESC;
The last two ANDs obviously will not work with what I am trying to do but how would can get this to work?
Thanks
Robert
I think you are close. you have a hybrid old ANSI vs more explicit JOIN. As you can see in this example, each one of the 3 criteria is joined to the meta table on all 3 parts explicitly. So, the ID should only appear ONCE per ID as each of the meta SHOULD AT MOST return one record, and the record will only qualify if all 3 meta find the respective match.
SELECT
ID
FROM
wp_posts
INNER JOIN wp_postmeta m1
ON wp_posts.ID = m1.post_id
AND m1.meta_key = 'a_property_type'
AND m1.meta_value = '$meta_type'
INNER JOIN wp_postmeta m2
on wp_posts.ID = m2.post_id
AND m2.meta_key = 'a_bedrooms_min'
AND m2.meta_value <= '$meta_bedrooms'
INNER JOIN wp_postmeta m3
on wp_posts.ID = m3.post_id
AND m3.meta_key = 'a_bedrooms_max'
AND m3.meta_value >= '$meta_bedrooms'
WHERE
wp_posts.post_type = 'alerts'
AND wp_posts.post_status = 'publish'
GROUP BY
wp_posts.ID
ORDER BY
wp_posts.post_date DESC;
I've revised as I believe the <= and >= were actually backwards. For example, your MINIMUM bedrooms from the meta_value was 1 and max is 3... you are LOOKING for 2 to be qualified.
So, you NEED MIN_BEDROOMS <= 2 and the 2 <= MAX_BEDROOMS
what was originally presented is MIN_BEDROOMS >= 2 (1 is never greater than 2)
and MAX_BEDROOMS <= 2 (2 is never less than 2).
Try again.

SQL WordPress Custom Query Inner Join

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