Compare date from sql (longtext) by an integer from php - 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}?

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;

mysql muliple joins from same colums with different keys

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'

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

Error return data null when using "case ... when" in php mysql?

I have a sample data:
users(id, name)
1 | peter
...
usermeta(user_id, meta_key, meta_value)
1 | level | 10
1 | display | pc
...
points(user_id, type, point)
1 | like | 5
2 | comment| 10
...
And mysql:
SELECT u.*,
(case when m.meta_key = 'level' then m.meta_value end) level ,
p.points AS point
FROM users u
LEFT JOIN points p ON p.user_id = u.id
LEFT JOIN usermeta AS m ON m.user_id = u.id
Result level = NULL, how to fix it?
id | name | level | point
1 | peter| NULL | 5
1 | peter| 10 | 10
Put m.meta_key = 'level' as the join condition.
SELECT u.*,
m.meta_value AS level ,
p.points AS point
FROM users u
LEFT JOIN points p ON p.uid = u.id
LEFT JOIN usermeta AS m ON m.user_id = u.id AND m.meta_key = 'level'
Have you tried providing an ELSE clause to your CASE? And according to your tables p.uid doesn't exist, it should be p.user_id, right?
Also, you should use INNER JOIN in this case as you want to retrieve only those cases in which the id field in the users table matches the ones in points and usermeta respectively. This should work properly:
SELECT
u.*,
CASE WHEN m.meta_key = 'level' THEN m.meta_value ELSE NULL END AS level,
p.points AS point
FROM users u
INNER JOIN points p ON p.user_id = u.id
INNER JOIN usermeta m ON m.user_id = u.id

Categories