Mysql : find nearest Maximum value, if not find nearest minimum value - mysql

I am having db structure like this -
Table : Tasks
Columns:
TaskID, TaskDescription, TaskCreatedDate
So if I want to find TaskCreatedDate having nearest greater value to current timestamp, and if there is no any task present having greater value than current timestamp then find TaskCreatedDate having nearest smaller value to current timestamp. This should done in single query. How can I do this?
Please help. Thanks in advance

-- IN ONE GO
select t.*, coalesce( (select TaskCreatedDate from Tasks where TaskCreatedDate > t.TaskCreatedDate ORDER BY TaskCreatedDate ASC LIMIT 1),
(select TaskCreatedDate from Tasks where TaskCreatedDate < t.TaskCreatedDate ORDER BY TaskCreatedDate DESC LIMIT 1)
)
from Tasks as t
coalesce allow us to return second condition when first is null

As we do not have a piece of code to to well understand, you can try:
select *
from Tasks
order by cast([TaskDueDate] as datetime) asc
CAST or CONVERT convert explicit a value form one data type to another.

This requires a clever order by clause:
select *
from tasks
order by TaskCreatedDate > now() desc,
(case when TaskCreatedDate > now() then TaskCreatedDate end) asc,
(case when TaskCreatedDate < now() then TaskCreatedDate end) desc
limit 1;
Depending on how your types are defined, you might want to use CURRENT_TIMESTAMP instead of now().
The first clause in the order by orders future dates first, then past dates. The second puts the future dates in order by TaskCreatedDate in ascending order, so the closest one is first. If there are none, then the third comes into play, ordering the rest descending.

Grab a union of the first later-than-now task (if any), plus the last earlier-than-now task, and keep the first result.
(select TaskId, TaskDescription, TaskDueDate from Tasks
where TaskDueDate >= now()
order by TaskDueDate
limit 1)
union
(select TaskId, TaskDescription, TaskDueDate from Tasks
where TaskDueDate < now()
order by TaskDueDate desc
limit 1)
limit 1

What about this?
SELECT *
FROM Tasks
ORDER BY (CASE WHEN TIMEDIFF(TaskDueDate, CURRENT_TIMESTAMP()) > 0 THEN 0 ELSE 1 END),
(CASE WHEN TIMEDIFF(TaskDueeDate, CURRENT_TIMESTAMP()) > 0
THEN TIMEDIFF (TaskDueeDate, CURRENT_TIMESTAMP())
ELSE TIMEDIFF (CURRENT_TIMESTAMP(), TaskDueDate)
END)
That will give you all the tasks with future due dates, ordered by increasing date, followed by those with due dates in the past ordred by decreasing date. If you only want one task, add LIMIT 1 at the end

Related

get the timestamp for the minimum/maximum value

i am new in learning sql. how to create query to get the timestamp of a minimum value and the minimum value itself?
previously i managed to get the minimum value but not with its timestamp. with this query
SELECT min(score) as lowest
FROM rank
WHERE time >= CAST(CURDATE() AS DATE)
here is the table that i've created:
(cannot attach image because of the reputation rule)
sorry for the bad english.
If you either expect that there would be only a single record with the lowest score, or if there be ties, you don't care which record gets returned, then using LIMIT might be the easiest way to go here:
SELECT timestamp, score
FROM rank
WHERE time >= CAST(CURDATE() AS DATE)
ORDER BY score
LIMIT 1;
If you care about ties, and want to see all of them, then we can use a subquery:
SELECT timestamp, score
FROM rank
WHERE time >= CAST(CURDATE() AS DATE) AND
score = (SELECT MIN(score) FROM rank WHERE time >= CAST(CURDATE() AS DATE));
It's possible by following way.
Note: It only works if you want to get a single record at once
select score, time
FROM rank
WHERE time >= CAST(CURDATE() AS DATE)
ORDER BY score ASC LIMIT 1

Select dates from the future or past, if future is not available

I have a simple table for events with a date column. I can easily select
the next n events with (assuming n = 3):
SELECT * FROM events WHERE `date` > NOW() ORDER BY `date` LIMIT 3
However, not aways there will be 3 events in the future. In this case,
I'd like to return the ones available in the future and complete what is
missing with the closest ones to today. E.g., if today is day 12-04, the
following dates marked with a * should be selected of the whole list:
10-03
20-03
30-03 *
10-04 *
20-04 *
While I can easily check the result of the first query to find out how
many rows were returned and build another query to find the past dates
if necessary, I'm interested to know if there is a way to fetch these
rows in a single query.
You can use multiple keys in the order by. So:
SELECT e.*
FROM events
ORDER BY (date > now()) DESC, -- future events first
(CASE WHEN date > now() THEN date END) ASC -- nearest future events first
date DESC -- other dates in descending order
LIMIT 3;
If your table is large, it is probably faster to get three events from the near future and near past and combine those:
select e.*
from ((select e.*
from events e
where date > now()
order by date asc
limit 3
) union all
(select e.*
from events e
where date <= now()
order by date desc
limit 3
)
) e
order by date desc
limit 3;

Sort records by; future ASC, past DESC

I want to sort records as follows:
Future/present events ASC
Past events DESC
So first today, then tomorrow, until there are no more future records.
Then I want to show the past events, but the latest first.
So far I've found a solution for the first point:
ORDER BY (
CASE WHEN ev.StartDate < CURDATE()
THEN 1
ELSE 0
END) ASC, ev.StartDate ASC
But the issue with this query is that all posts are ordered ASC, including the past posts (which need to be DESC).
How do I combine this in the CASE?
You need a slightly more complex order by:
ORDER BY (ev.StartDate < CURDATE()),
(case when ev.StartDate > CURDATE() then ev.StartDate end) ASC,
(case when ev.StartDate < CURDATE() then ev.StartDate end) DESC
You could actually do this with two clauses:
ORDER BY greatest(ev.StartDate, CURDATE()) DESC,
least(ev.StartDate, CURDATE()) ASC
But I think the first version is clearer in its intention.
I find this most straight forward, without needing complex conditional syntax:
first one ranks future before past, second one orders the future ASC, third one orders the past DESC
(second and third ones are interchangeable)
ORDER BY
(date < CURDATE()) ASC,
(greatest(date, CURDATE()) ASC,
(least(date, CURDATE()) DESC
ORDER BY
CASE WHEN (CURDATE() > ev.StartDate)
THEN datediff(CURDATE(),ev.StartDate ) --Past, older date bigger differ
ELSE datediff(ev.StartDate , CURDATE()+100) END --Future, differ from a more futrue date
I had the same requirement and found another way
ORDER BY (CURDATE()>ev.StartDate) ASC, ABS(DATEDIFF(CURDATE(),ev.StartDate))

Sort by closest date, then past

I have an art gallery page featuring a panel that shows up to 5 exhibitions 'At this gallery'. Sometimes there could be many on, it should show the current or upcoming first, then resort to past exhibitions.
Is it possible to do in one query?
The following will give me those closest in terms of date difference but I can end up getting past events at the top of the list (because if it's a day past, it's obviously less than the upcoming event in 5 days)
SELECT DATEDIFF(date_start, NOW()) as date_diff, exhibitions.*
FROM events
WHERE event.gallery_id = XX
ORDER BY date_diff asc
LIMIT 5
The date_diff is returning negative values for past events. This is one fix:
SELECT DATEDIFF(date_start, NOW()) as date_diff, exhibitions.*
FROM events
WHERE event.gallery_id = XX
ORDER BY (case when date_diff < 0 then 1 else 0 end),
abs(date_diff) asc
LIMIT 5
You can order by date_start < CURDATE() which will return either 0 or 1, 0 being future, 1 being past, and if you order the result ASC then it would ensure future ones always show up before past ones
for example:
ORDER BY
(date_start < CURDATE()) ASC,
(greatest(date_start, CURDATE()) ASC,
(least(date_start, CURDATE()) DESC
first one ranks future before past, second one orders the future ASC (closest first), third one orders the past DESC (oldest first)
(second and third ones are interchange)
Normally you cant sort one column in both directions. If you want yo order by upcoming ASC the past DESC you can do it with trick:
SELECT yourDateColumn,IF(yourDateColumn)<NOW(),DATEDIFF(yourDateColumn),NOW()) * -1000000000,UNIX_TIMESTAMP(STR_TO_DATE(CONVERT_TZ(e.startDate,e.timeZone,'Europe/Paris'), '%Y-%m-%d'))) AS diff FROM yourTable ORDER BY DIFF ASC;

SQL Query to check rate limit

Lets say I have a table of messages that users have sent, each with a timestamp.
I want to make a query that will tell me (historically) the most number of messages a user ever sent in an hour.
So in other words, in any given 1 hour period, what was the most number of messages sent.
Any ideas?
Assuming timestamp to be a DATETIME - otherwise, use FROM_UNIXTIME to convert to a DATETIME...
For a [rolling] count within the last hour:
SELECT COUNT(*) AS cnt
FROM MESSAGES m
WHERE m.timestamp BETWEEN DATE_SUB(NOW(), INTERVAL 1 HOUR)
AND NOW()
GROUP BY m.user
ORDER BY cnt DESC
LIMIT 1
If you want a specific hour, specify the hour:
SELECT COUNT(*) AS cnt
FROM MESSAGES m
WHERE m.timestamp BETWEEN '2011-06-06 14:00:00'
AND '2011-06-06 15:00:00'
GROUP BY m.user
ORDER BY cnt DESC
LIMIT 1
Need more details on table structure etc. but something like:
select date(timestmp), hour(timestmp) , count(*)
from yourtable group by date(timestmp) , hour(timestmp)
order by count(*) DESC
limit 100;
would give you hte desired result.
Something like this should work:
SELECT MAX(PerHr) FROM
(SELECT COUNT(*) AS PerHr FROM messages WHERE msg_uid=?
GROUP BY msg_time/3600) t
I suspect this would be horribly slow, but for an arbitrary historical max hour, something like this might work (downvote me if I'm way off, I'm not a MySQL person):
SELECT base.user, base.time, COUNT(later.time)
FROM messages base
INNER JOIN messages later ON later.time BETWEEN base.time AND DATE_ADD(base.time, INTERVAL 1 HOUR) AND base.user = later.user
WHERE base.user = --{This query will only work for one user}
GROUP BY base.user, base.time
ORDER BY COUNT(later.time) DESC
LIMIT 1