MYSQL: closest to supplied date grouped by user_id - php

I need to get multiple rows with a date_added closest to but not past a user supplied date, grouped by user_id.
I've looked at a bunch of max in group type answers but I'm not quite there:
Get nearest records to specific date grouped by type
SQL Query to show nearest date?
Find closest datetime to specified datetime in mysql query
Get closest date from MySQL table
https://dev.mysql.com/doc/refman/5.0/en/example-maximum-column-group-row.html
This is close: https://stackoverflow.com/a/17038667/5319244. Finds the max date though, I need the max specified by user input, not max outright.
Here's a subset of the data with the correct organisation_id, framework_id and level_id already filtered.
+----+---------+-----------------+--------------+----------+---------------------+
| id | user_id | organisation_id | framework_id | level_id | date_added |
+----+---------+-----------------+--------------+----------+---------------------+
| 2 | 1 | 2 | 1 | 1 | 2015-07-31 14:02:49 |
| 9 | 2 | 2 | 1 | 1 | 2015-09-01 11:05:09 |
| 11 | 1 | 2 | 1 | 1 | 2015-09-07 14:13:39 |
+----+---------+-----------------+--------------+----------+---------------------+
If the supplied date is 2015-09-07. I'd expect to see id's: 9 and 11.
If the supplied date is 2015-09-01. I'd expect to see id's: 2 and 9.
If the supplied date is 2015-07-31. I'd expect to see id: 2.
This query is as close as I got:
SELECT t1.id
, t1.user_id
, t1.date_added
FROM
completed_frameworks AS t1
WHERE date_added = (
SELECT
MAX(date_added)
FROM
completed_frameworks
WHERE
user_id = t1.user_id
AND
date_added <= '2015-09-07 23:59:59'
)
AND
(
t1.organisation_id = 2
AND
t1.framework_id = 1
AND
t1.level_id = 1
)
It returns what I expect for the date: 2015-09-07
When the date is 2015-09-01 however it only returns id 9. Not also 2 as I'd expect.
When the date is 2015-07-31 it returns 0 rows..
Let me know if I there's anything else I can provide.
Cheers!
EDIT:
Thanks for the replies thus far. I need to clarify two points:
1) I don't have a limit. I'm expecting those rows due to the user id's. There could be n users returned. I just want a row for each user where the date_added is closest to the user supplied date.
2) The date supplied will not have a time value. It will be from a simple datepicker UI. In my example query I've added the time of 23:59:59 to encompass all of that day.

Try this subquery instead. The organisation_id/framework_id/level_id filter is moved into the subquery and it now returns the right values for the examples you've given.
SELECT t1.id, t1.user_id, t1.date_added
FROM
completed_frameworks AS t1
WHERE date_added = (
SELECT
MAX(date_added)
FROM
completed_frameworks
WHERE
user_id = t1.user_id
AND
date_added <= '2015-09-01 23:59:59'
AND
organisation_id = 2
AND
framework_id = 1
AND
level_id = 1
)

Here is a sample query you could use for an input date of '2015-09-07 14:13:39'. The inner query returns the input date along with the next highest date. This temporary table is then used to filter completed_frameworks to give you only records from the two dates most recent to the input date.
SELECT t1.id, t1.user_id, t1.date_added
FROM completed_frameworks t1
INNER JOIN
(
SELECT cf.date_added
FROM completed_frameworks cf
GROUP BY cf.date_added
HAVING cf.date_added <= '2015-09-07 14:13:39'
ORDER BY cf.date_added DESC
LIMIT 2
) t2
ON t1.date_added = t2.date_added
WHERE t1.organisation_id = 2 AND t1.framework_id = 1 AND t1.level_id = 1

I think this does what you want:
SELECT f.id, f.user_id f.date_added
FROM completed_frameworks f
WHERE date(f.date_added) <= '2025-09-07' -- or whatever your date is
ORDER BY f.date_added DESC
LIMIT 2;
EDIT:
If you want one date per user id that is closest to the specified date, I would suggest:
select f.*
from completed_frameworks f join
(select user_id, max(date_added) as maxda
from completed_frameworks
where date(date_added) <= '2015-09-07'
group by user_id
) u
on f.user_id = u.user_id and f.date_added = maxda

Related

How to left join selecting value with the highest user_id

well this question is related to my previous question How to left join 2 tables with SUM() and MAX() grouped by date
what i changed is i added user_id column (auto incremented) and want to select value with highest user_id per date
i have table loadhistory ( wanted to "select only value with highest user_id per date" group by and order by date DESC.)
so in this case, i want to select 150 for 2015-02-27 since it has the highest user_id in that date and 50 for 2015-02-28
| user_id | customer_id | date | bal |
1 1 2015-02-27 100
2 1 2015-02-27 150
3 1 2015-02-28 150
4 1 2015-02-28 50
and table transactionrecord (want to sum up values per date using SUM(bal) group by and order by date DESC)
| user_id |customer_id | date | bal |
1 1 2015-02-27 50
2 1 2015-02-27 20
3 1 2015-02-28 10
And i want to join the 2 tables which would look like this:
| date | balance | amount paid |
2015-02-28 50 10
2015-02-27 150 70
this is the code so far (i used the code i got from my previous question and edited it here in my new question hoping to arrive desired result but did not)
SELECT a.customer_id, a.date, (b.bal AS bal WHERE b.user_id = MAX(b.user_id) , a.paid
FROM (
SELECT customer_id, date, SUM(bal) AS paid
FROM transactionrecord
GROUP BY customer_id, date
) AS a LEFT JOIN loadhistory AS b
ON a.customer_id = b.customer_id AND a.date = b.date
WHERE a.customer_id = 1
GROUP BY a.customer_id, a.date, a.paid
ORDER BY a.date DESC
help please. thanks in advance
MySQL use the first row by group by, so you must order it before you can use group by like this:
SELECT * FROM (SELECT * FROM `loadhistory` ORDER BY user_id DESC) history GROUP BY date
So you can use the following query as solution:
SELECT h.date,h.bal as balance, t.amount as 'amount paid' FROM
(SELECT * FROM (SELECT * FROM `loadhistory` ORDER BY user_id DESC) history GROUP BY date) as h
JOIN
(SELECT SUM(bal) as amount, customer_id, date FROM `transactionrecord` GROUP BY date) as t
ON h.date = t.date AND h.customer_id = t.customer_id
ORDER BY date DESC

GROUP BY not working well

Hello i have a table with:
id_order | id_order_history | date_add
With this query i can get all orders that have id_order_history = 3 (in preparation)
SELECT
`oh`.`id_order`
FROM (
SELECT MAX( `date_add` ) AS date_add
FROM `'._DB_PREFIX_.'order_history`
GROUP BY `id_order`
) AS h
INNER JOIN
'._DB_PREFIX_.'order_history AS oh
ON
`h`.`date_add` = `oh`.`date_add`
INNER JOIN
'._DB_PREFIX_.'orders AS o
ON
`oh`.`id_order` = `o`.`id_order`';
$sql .= 'WHERE
oh.`id_order_state` = '.$status.'';
$sql .= ' AND
oh.date_add >= "'.$start.'"';
$sql .= ' AND
oh.date_add <= "'.$end.'"
ORDER BY
`oh`.`id_order` ASC
The problem is that for some order the SELECT MAX don't get the correct value of date_add
id_order | id_order_history | date_add
1 | 5 | 25-09-2014 18:07:59 ---> this is the correct value
1 | 4 | 25-09-2014 18:07:51
1 | 3 | 25-09-2014 18:06:42 ---> but query return this
If I add on date 1 second (18:06:43) the query return the correct value 25-09-2014 18:07:59
I just can not figure out how to solve the problem.
I have read around that sometimes GROUP BY takes results higgledy-piggledy ...
how can I fix it?
Convert your column date_add to a ISO datetimestring and/or use TO_SECONDS (see MySQL doc here)
...( SELECT FROM_UNIXTIME(MAX(TO_SECONDS(`date_add`)) ) AS date_add
FROM `'._DB_PREFIX_.'order_history`
GROUP BY `id_order` ) AS h

MySQL Order by sum of another table

I currently have 2 tables in mysql; comments and comment_rating
comment_rating has the following structure:
+------------+
| Field |
+------------+
| id |
| comment_id |
| user_id |
| positive |
| created_at |
+------------+
The field positive is either 1 or -1 1 being a positive (up vote) and -1 being negative (down vote)
I have this current query which will get me the most rated on comment:
SELECT *, COUNT(comment_rating.id) AS rating_count FROM comments LEFT JOIN comment_rating ON comments.id = comment_rating.comment_id GROUP BY comments.id ORDER BY rating_count DESC
I Need to know how (using mysql query) I am able to get comments ordered by best rating;
Meaning ordered by the sum of from the rating per comment.
Example:
Comment X has 2 upvotes and 4 downvotes (grand total of -2)
Comment Y has no votes (grand total of 0)
Comment Z has 1 upvote (grand total of 1)
The order these will come out will be:
Comment Z
Comment Y
Comment X
SELECT comments.id,
COUNT(comment_rating.id) AS rating_count,
sum(positive) as rating
FROM comments
LEFT JOIN comment_rating ON comments.id = comment_rating.comment_id
GROUP BY comments.id
ORDER BY rating DESC
Thank you juergen d
Your query was almost perfect:
here is a slightly modified version which will do the ordering correctly:
SELECT comments.id,
COUNT(comment_rating.id) AS rating_count,
COALESCE(SUM(positive),0) as rating
FROM comments
LEFT JOIN comment_rating ON comments.id = comment_rating.comment_id
GROUP BY comments.id
ORDER BY rating DESC
This way if the row doesnt exist it will be set to zero rather than null and will be ordered correctly.

select one winner of highest score of every one month

I need to select highest scorer as a monthly winner. I want to show previous winners too. My current query selects only previous one month winner, but how can I select all previous monthly winners?
my query:
function month_winner_now()
{
$query = $this->db->query("SELECT winner.id,winner.score,winner.user_id,winner.date, user.id,user.username,user.email,user_profile.user_image,user_profile.hometown,user_profile.country FROM `winner` LEFT JOIN `user` ON user.id = winner.user_id LEFT JOIN `user_profile` ON user_profile.user_id = winner.user_id WHERE MONTH(CURDATE())= MONTH(winner.date) ORDER BY winner.score DESC
LIMIT 1");
return $query->result_array();
}
My current output :
"monthly winners":[
{
"id":"5",
"score":"1256",
"user_id":"5",
"date":"2014-03-05",
"username":"",
"email":"",
"user_image":"",
"hometown":"",
"country":""
}
But I need output like
"monthly winners":[
{
"id":"4",
"score":"233",
"user_id":"4",
"date":"2014-03-02",
"username":"Mahmudul Hasan Swapon",
"email":"",
"user_image":"",
"hometown":"",
"country":""
},
{
"id":"7",
"score":"123",
"user_id":"7",
"date":"2014-03-04",
"username":"Prosanto Biswas",
"email":"",
"user_image":"",
"hometown":"",
"country":""
}
],
Monthly winners json array shows previous all month winners but every month should have one winner.
DB table look like
id | name | userid | score | date |
------------------------------------------------------------
1 | john | 1 | 44 | 2013-03-2
2 | mary | 2 | 59 | 2013-03-5
3 | john | 12 | 38 | 2013-03-8
4 | elvis | 3 | 19 | 2013-02-10
5 | john | 11 | 1002 | 2013-01-11
6 | johnah | 10 | 200 | 2013-01-11
I recreated sql query and added one more field "month_of_year", now I think it will be helpful for you according to your requirement
SELECT
winner.id,winner.score,winner.user_id,winner.date,
user.id,user.username,user.email,user_profile.user_image,
user_profile.hometown,user_profile.country,
date_format( winner.date, '%Y-%m' ) AS month_of_year
FROM
`winner`
LEFT JOIN `user`
ON user.id = winner.user_id
LEFT JOIN `user_profile`
ON user_profile.user_id = winner.user_id
GROUP BY month_of_year
ORDER BY winner.score DESC
i want to show previous all month winner
You are checking for equality of month hence other months (previous or later) are omitted. Change comparison to <= or >= as the case may be.
And if you use LIMIT 1 there is a chance that other month details are not fetched if exists a record in current month.
Try this:
SELECT
winner.id -- etc fields
FROM
`winner`
LEFT JOIN `user`
ON user.id = winner.user_id
LEFT JOIN `user_profile`
ON user_profile.user_id = winner.user_id
WHERE
date_format( winner.date, '%Y%m' ) <= date_format( CURDATE(), '%Y%m' )
ORDER BY
winner.score DESC
For the winner from previous month:
$query = $this->db->query("
SELECT winner.id,winner.score,winner.user_id,winner.date, user.id,user.username,user.email,user_profile.user_image,user_profile.hometown,user_profile.country
FROM `winner`
LEFT JOIN `user`
ON user.id = winner.user_id
LEFT JOIN `user_profile`
ON user_profile.user_id = winner.user_id
WHERE MONTH(DATE_SUB(CURDATE(), INTERVAL 1 MONTH)) = MONTH(winner.date) ORDER BY winner.score DESC LIMIT 1
");
Try something like this query:
SELECT * FROM user
INNSER JOIN
(SELECT user_id, MAX(score) AS s, MONTH(date) AS d
FROM winner
GROUP BY MONTH(date)) monthlyWinner ON (user.id = monthlyWinner.user_id)
Try this,
;with cte as
(
select Datename(mm,[date]) as m
--,max(amount_paid)
,Rank() over(PARTITION BY Datename(mm,[date]) order by [score] desc) as rr
,[score]
,id
from myTbl
--where DATEDIFF(YY,[date],'1/1/2013') = 0
)
select * from cte
left join myTbl as r on r.id=cte.id
where rr = 1

MYSQL sorting and limit using a certain row and selecting previous and next rows

I have a simple table called users with the following data:
id | hops
1 | 3
2 | 1
3 | 5
4 | 2
5 | 4
6 | 5
I want to select the number of hops of any given id that I specify and also select the next and previous ids according to the number of hops sorted from highest to lowest.
To explain more I use the following query:
SELECT * FROM test WHERE id = 1
OR (id > 1 AND hops >= (SELECT hops FROM test WHERE id= 1) )
OR (id < 1 AND hops <= (SELECT hops FROM test WHERE id= 1) )
LIMIT 3
So in the above query I tried to get id=1, next id with the same or higher number of hops, and the previous id with the same or lower number of hops.
This is the result i get:
id | hops
1 | 3
3 | 5
5 | 4
As you can see it selected id=1 and two higher ids although I want only one higher id and one lower id. So, in this case the result should be like this instead:
id | hops
1 | 3
3 | 5
As there is no lower id than 1, so nothing lower to fit the criteria and selects only 1 higher id. The wrong result is because of using LIMIT 3 but I can't use a LIMIT for each condition. So don't know how to approach this at all.
Have another question, would using the sub-query
"SELECT hops FROM test WHERE id= 1"
slow down the server on a large scale?? I heard that it's not preferable to use sub-queries but have no other way to get this number except using a separate query.
Thanks
here you go, change the order by ID according to your liking...you didn't say if you wanted the closest number of hops or the closest ID, just one greater or lower
SELECT * FROM test
WHERE id IN (1,(
SELECT id FROM test WHERE id > 1 AND hops >= (
SELECT hops FROM test WHERE id = 1
) ORDER BY id LIMIT 1
), (
SELECT id FROM test WHERE id < 1 AND hops <= (
SELECT hops FROM test WHERE id = 1
) ORDER BY id DESC LIMIT 1
))
If I understand your question correctly, I believe the following will work.
-- Previous Rec
SELECT t2.*
FROM test as t1
JOIN test as t2 ON t2.hop <= t1.id
WHERE t1.id = 1
ORDER BY t2.id DESC
LIMIT 1
UNION ALL
-- Current Rec
SELECT *
FROM test as t
WHERE id = 1
UNION ALL
-- Following Rec
SELECT t2.*
FROM test as t1
JOIN test as t2 ON t2.id >= t1.hop
WHERE t1.id = 1
ORDER BY t2.id ASC
LIMIT 1

Categories