mysql muliple joins from same colums with different keys - php

I need some help making the second join with the tables below. I got some help from here previously where it was suggested I need to add a second JOIN, however, this is where I'm stuck and need some assistance.
wp-posts
-----------------
id | Post_Title |
-----------------
01 | Event 1 |
-----------------
02 | Event 2 |
-----------------
wp-postmeta
-------------------------------------------------------
meta_id | post_id | meta_key | meta_value |
-----------------------------------------------------
456 | 01 | _EventStartDate | 01/01/2017 |
-------------------------------------------------- ---
789 | 01 | _EventEndDate | 05/02/2017 |
-----------------------------------------------------
The end result I'm after is something like;
Title - starts on <_EventStartDate > and ends on <_EventEndDate>
and im using the following to get the data:
$result = $wpdb->get_results ( "
SELECT $wpdb->posts.ID, $wpdb->posts.post_content, $wpdb->postmeta.meta_id,
$wpdb->postmeta.post_id, $wpdb->postmeta.meta_key, $wpdb->postmeta.meta_value, $wpdb->posts.post_title
FROM $wpdb->posts INNER JOIN $wpdb->postmeta ON $wpdb->posts.ID = $wpdb->postmeta.post_id
WHERE $wpdb->postmeta.meta_key = '_EventStartDate'
ORDER BY $wpdb->postmeta.meta_value " );
Now I've been told that the WHERE will just return a single row and that I need to make the second JOIN using $wpdb->postmeta.meta_key = '_EventStartDate' but after hours of trying to implement this I'm unable to get and data back at all.
If someone could help solve this it would be immensely helpful as I have a few more queries I would like to write and I'm guessing I will need to use the same principal with them too.
Thanks for reading!!

You can do it with a single inner join, grouping by the post ID and then splitting start date and end date:
select
wpposts.post_content,
substring_index(GROUP_CONCAT(meta_value order by str_to_date(meta_value,'%d/%m/%Y')), ',', 1) as start_date ,
substring_index(GROUP_CONCAT(meta_value order by str_to_date(meta_value,'%d/%m/%Y')), ',', -1) as end_date
from wpposts inner join wppostmeta
on wpposts.id = wppostmeta.post_id
where wppostmeta.meta_key='_EventStartDate' or wppostmeta.meta_key='_EventEndDate'
group by wppostmeta.post_id

You can get the start_date and end_date in one row using group by like this:
select
t1.*,
max(case when meta_key = '_EventStartDate' then meta_value end) start_date,
max(case when meta_key = '_EventEndDate' then meta_value end) end_date
from wp_posts t1
inner join wp_postmeta t2
on t1.id = t2.post_id
group by t1.post_id;
if ONLY_FULL_GROUP_BY is enabled, change the group by clause to:
group by t1.post_id, t1.Post_title;

Assuming there is only one _EventStartDate and one _EventEndDate per post in wp-postmeta, a simplified query would be:
SELECT p.post_title, pm1.meta_value AS start_date, pm2.meta_value AS end_date
FROM wp-posts p
INNER JOIN wp-postmeta pm1
ON p.post_id = pm1.post_id
AND pm1.meta_ekey = '_EventStartDate'
INNER JOIN wp-postmeta pm2
ON p.post_id = pm2.post_id
AND pm2.meta_ekey = '_EventEndDate'

Related

MySQL Query On The Same Table Different Columns

I have two tables: Project and ProjectFieldValue
I am needing to return results from the ProjectFieldValue based on multiple different key/value options in the table. I can get it to work with one key/value pair, but once I add another AND statement to the query it returns nothing.
Here is a sample of my tables followed by my query...
Project Table
----------------------
id | name
----------------------
1 | Project #1
ProjectFieldValue Table
I have millions of records like this and they are all stored in this table and associated to a specific Project.
----------------------------------------------------------------------------------------
id | project_id | text_value | date_value | field_key
----------------------------------------------------------------------------------------
1 | 1 | Active | NULL | contract_status
2 | 1 | NULL | 2020-06-02 00:01:58 | listing_date
3 | 1 | Seller | NULL | contract_client_type
4 | 1 | Active | NULL | contract_option
Here are my queries broken down by what works and doesn't work:
This does work, but, it is searching on 1 key/value pair...
SELECT p.name, p.id
FROM ProjectFieldValue pfv
LEFT JOIN Project p
ON pfv.project_id = p.id
WHERE (pfv.text_value IN ( SELECT text_value FROM ProjectFieldValue WHERE text_value IN ('Active')) AND field_key = 'contract_status')
GROUP BY p.id
This doesn't work because it is searching on 3 key/value pairs...
SELECT p.name, p.id
FROM ProjectFieldValue pfv
LEFT JOIN Project p
ON pfv.project_id = p.id
WHERE (pfv.text_value IN ( SELECT text_value FROM ProjectFieldValue WHERE text_value IN ('Active')) AND field_key = 'contract_status')
AND (pfv.text_value IN ( SELECT text_value FROM ProjectFieldValue WHERE text_value IN ('Seller')) AND field_key = 'contract_client_type')
AND (pfv.date_value between '2020-07-08 00:00:00' AND '2020-07-11 23:59:59' AND pfv.field_key = 'listing_date')
GROUP BY p.id
Goal
Ultimately, what I would need to be able to do is search on unlimited key/value pairs in this table and return all results grouped by the p.id
Thanks for your help!
This should do the thing.
SELECT p.name, p.id
FROM ProjectFieldValue pfv
LEFT JOIN Project p
ON pfv.project_id = p.id
WHERE (field_key = 'contract_status' AND pfv.text_value = 'Active')
OR (field_key = 'contract_client_type' AND pfv.text_value = 'Seller')
OR (pfv.field_key = 'listing_date' AND pfv.date_value between '2020-07-08 00:00:00' AND '2020-07-11 23:59:59')
GROUP BY p.id;
But I have doubt why you are Left joining Project and ProjectFieldValue. A simple Inner Join should solve your purpose. As you are grouping by on p.id. You may encourage a lot of NULLed columns. So I would suggest below.
SELECT p.name, p.id
FROM Project p
JOIN ProjectFieldValue pfv
ON p.id = pfv.project_id
WHERE (field_key = 'contract_status' AND pfv.text_value = 'Active')
OR (field_key = 'contract_client_type' AND pfv.text_value = 'Seller')
OR (pfv.field_key = 'listing_date' AND pfv.date_value between '2020-07-08 00:00:00' AND '2020-07-11 23:59:59')
GROUP BY p.id;

Compare date from sql (longtext) by an integer from php

On my project i have wordpress custom field data from mysql :
---------------------------------------------------
id | post_id | meta_key | meta_value
---------------------------------------------------
1 | 200 | age_min | 5
2 | 200 | age_max | 8
3 | 399 | ... | ...
My table structure
id => aut_increment
meta_key => varchar(255)
meta_value => longtext
From my sql script i have to find childs between 'age_min and age_max' and childs number :
SELECT DISTINCT p.ID
, a.meta_value as nbrmin, b.meta_value as nbr_enfants_min , c.meta_value as age_min, d.meta_value as age_max
FROM `6288gjvs_posts` as p
LEFT JOIN 6288gjvs_postmeta as a ON a.post_id = p.ID
LEFT JOIN 6288gjvs_postmeta as b ON b.post_id = p.ID
LEFT JOIN 6288gjvs_postmeta as c ON c.post_id = p.ID
LEFT JOIN 6288gjvs_postmeta as d ON d.post_id = p.ID
WHERE p.post_type = 'product' AND post_status = 'publish'
AND a.meta_key = 'childs_min_number'
AND a.meta_value <= 2
AND b.meta_key = 'childs_max_number'
AND b.meta_value >= 2
AND c.meta_key = 'age_min'
AND c.meta_value = 1
AND d.meta_key = 'age_max'
AND d.meta_value >= 10
When i launch this query, this not working properly, because meta_value type is a longtext and my php variables ( $this->age_min, $this->age_max ) are integers, so i cannot compare those 2 different type ?
I tried to convert like this with no luck :
... CONVERT(a.meta_value, DECIMAL) ...
as mentioned from this site : Mysql conversion functions
Anyone can help me please ?
Thank you.
Forgive me if I'm wrong but shouldn't it be AND b.meta_value >= {$this->age_max}?
you don't need to convert types
I think the error is in line
AND a.meta_value <= {$this->age_min}
should not it be AND a.meta_value >= {$this->age_min}?

COUNT number of rows on table with LEFT JOIN returns 1 when not exists

I've two tables, for example:
post:
id | author | content | date
1 | Lucas | Hello! | 2016
2 | Igor | Hi! | 2016
comment:
id | post_id | content | date
1 | 2 | hehehe | 2016
2 | 1 | hahaha | 2016
3 | 2 | huhuhu | 2016
And I to do a SELECT that return all posts and a COUNT of rows of all comments with post.id = comment.id.
So, I tried:
SELECT p.id, p.author, p.content, p.date, COUNT(*) AS numComments FROM post p LEFT JOIN comment ON p.id = post_id WHERE p.author = '$author' GROUP BY p.id DESC LIMIT 12
And I got do it. But, even when no exists comments with p.id = post_id he returns 1.
So, I tried:
SELECT p.id, p.author, p.content, p.date, CASE WHEN COUNT(*) < 1 THEN '0' ELSE COUNT(*) END AS numComments FROM post p LEFT JOIN comment ON p.id = post_id WHERE p.author = '$author' GROUP BY p.id DESC LIMIT 12
But the result is the same. How to do this?
As outer joins return a row even if there's no matching data you need to count a column from the inner table, usually it's the column used in join:
SELECT p.id, p.author, p.content, p.date, COUNT(post_id) AS numComments
FROM post p LEFT JOIN comment ON p.id = post_id
WHERE p.author = '$author'
GROUP BY p.id -- seems to be mysql, otherwise you need to add more columns to the list
If you don't want to show rows with a zero count simply switch to an
INNER JOIN.
you can get count by this way, also last is order by not group by:
SELECT p.id, p.author, p.content, p.date,
(select COUNT(*) from comment where p.id = comment.post_id) AS numComments FROM post p
WHERE p.author = '$author'
ORDER BY p.id DESC LIMIT 12

How to order by COUNT with SUM and MINUS of multi tables

I'm trying to show post by order them with sum of comment and like.
There are three table using in this query post,comment and like
for table like it has column type that keep value like or unlike.
SQL
SELECT (SELECT COUNT(id) AS count_comment
FROM comment WHERE comment.post_id = post.post_id),
(SELECT COUNT(id) AS count_like
FROM like WHERE like.post_id = post.post_id AND like.type = 'like'),
(SELECT COUNT(id) AS count_unlike
FROM like WHERE like.post_id = post.post_id AND like.type = 'unlike'),
post.* FROM post
ORDER BY (count_comment + count_like - count_unlike) DESC;
So, this is an example when it shows on the page
post_id | comment | like | unlike | (comment+like-unlike)
4 | 5 | 3 | 1 | 7
1 | 2 | 3 | 0 | 5
2 | 1 | 1 | 4 | -2
... | ... | ... | ... | ...
My problem is my SQL is very slow, please suggest another way if it can. I've tried to use JOIN but i can't figured out how its SQL should be, please help thanks.
Using a derived table for each of the counts, the query below counts comments, likes, unlikes for each post and then joins the counts to the post table by post_id.
SELECT
p.post_id,
COALESCE(c.comment_count,0) comment_count,
COALESCE(l.like_count,0) like_count,
COALESCE(ul.unlike_count,0) unlike_count,
(COALESCE(c.comment_count,0)
+ COALESCE(l.like_count,0)
- COALESCE(ul.unlike_count,0)) total
FROM post p
LEFT JOIN (
SELECT c.post_id,
COUNT(*) comment_count
FROM comment c
GROUP BY c.post_id
) c ON c.post_id = p.post_id
LEFT JOIN (
SELECT l.post_id,
COUNT(*) like_count
FROM like l
WHERE l.type = 'like'
GROUP BY l.post_id
) l ON l.post_id = p.post_id
LEFT JOIN (
SELECT ul.post_id,
COUNT(*) unlike_count
FROM like ul
WHERE ul.type = 'unlike'
GROUP BY ul.post_id
) ul ON ul.post_id = p.post_id
ORDER BY total DESC

PHP SQL join table rows as keys [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 8 years ago.
Improve this question
I was wondering if someone could help me. I have the following two tables:
---Posts----
id | title | content |
544 | Alan | Blah
---Postmeta----
metaid | post_id | meta_key | meta_value
1 | 544 | age | 45
2 | 544 | email | test#hotmail.co.uk
I want to join the two tables by post_id and then display the meta_keys. Something like this
SELECT posts.post_id,
postmeta.age,
postmeta.email,
FROM
posts
JOIN posts
ON posts.post_id = postmeta.post_id
Any help would be much appreciated. Cheers
You need to PIVOT the rows inside the table postmeta into columns, using CASE with MAX as an aggregate function to eliminate NULLs like so:
SELECT p.id, pm.age, pm.email
FROM posts p
JOIN
(
SELECT
post_id,
MAX(CASE WHEN meta_key = 'age' THEN meta_value END) AS age,
MAX(CASE WHEN meta_key = 'email' THEN meta_value END) AS email
FROM postsmeta
GROUP BY post_id
) pm ON p.id = pm.post_id
Here is a Demo in sql fiddle
Note that: You are currently storing all values for age and email in one column of the same data type that would be varchar for example then you have to cast the age values to INT in the query in order to get it like INT if you want to perform some calculations later on, and I left this part for you.
SELECT
posts.id,
postmeta.key,
postmeta.meta_value
FROM
posts
JOIN
postmeta ON posts.id = postmeta.post_id
WHERE
postmeta.key IN ('age', 'email')
it should be like this
SELECT posts.post_id,
postmeta.age,
postmeta.email,
FROM
posts
JOIN postsmeta
ON posts.post_id = postmeta.post_id
SELECT posts.id,
postmeta.age,
postmeta.email,
m.meta_key,
m.meta_value
FROM
posts p
JOIN Postmeta m
ON p.id = m.post_id
Instead of saving meta_key in a new row, try to make your table structure like this
metaid | post_id | age | email
1 | 544 | 45 | test1#hotmail.co.uk
2 | 545 | 51 | test2#hotmail.co.uk
3 | 546 | 20 | test3#hotmail.co.uk
4 | 547 | 26 | test4#hotmail.co.uk
you can add more fields to table structure.
and after that you can make a simple join query, that will save your time and resources to execute the query.
like this
SELECT posts.post_id, postmeta.age, postmeta.email FROM posts JOIN postmeta ON posts.post_id = postmeta.post_id;
Joining twice to the postmeta table:
SELECT p.id,
pm1.meta_value AS age,
pm2.meta_value AS email
FROM
posts AS p
LEFT JOIN
postmeta AS pm1
ON pm1.post_id = p.id
AND pm1.meta_key = 'age'
LEFT JOIN
postmeta AS pm2
ON pm2.post_id = p.id
AND pm2.meta_key = 'email' ;

Categories