Sort by closest date, then past - mysql

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;

Related

MYSQL sort date by future dates first in ASC and old dates then in DESC

I want to sort dates in MYSQL in a way such that, future dates will be sorted first by in ASC order and, then then old dates last in DESC order. Here is the query I used to do, but both date types (future and old) are sorted out in ASC order. How can I fix this?
SELECT id, end_date FROM employee
ORDER BY CASE WHEN DATE(date) > DATE(NOW())
THEN 0
ELSE 1 END, date ASC
First, sort by the boolean expression date <= CURRENT_DATE so that all future dates are on the top and then sort by the absolute difference to the current date:
SELECT *
FROM employee
ORDER BY date <= CURRENT_DATE,
ABS(DATEDIFF(date, CURRENT_DATE));
See the demo.

SQL date not relative

I have a table in which i store every 15 minutes result of a cron job, which is nothing more than a timestamp, a population number and an id.
I am trying to query it as following.
SELECT ROUND(AVG(`population`),0) AS population, DATE(`time`) AS date
FROM `swg_servertracker`
WHERE `time` >= DATE(NOW()) - INTERVAL 7 DAY
GROUP BY DATE(`time`)
DESC
LIMIT 7
What it does it creates an daily average, and grabs the last 7 entries. Sadly in was not in the right order, so i flipped it to ascending. My problem is when i inverse (asc) it, it skips today, and goes back an extra day (today is the 3rd of october, which is not taken in the equation when i use the ascending)
I tried to set the where statement to just now - interval 168 hours (which is also 7 days but then relative back) which had no result either on this. Still it skips today and just goes back 7 days from on yesterday.
SELECT ROUND(AVG(`population`),0) AS population, DATE(`time`) AS date
FROM `swg_servertracker`
WHERE `time` >= NOW() - INTERVAL 168 HOUR
GROUP BY DATE(`time`)
ASC
LIMIT 7
So is there a way I can take today in account as well?
You select 8 records instead 7 records. If you want to select 7 latest records, you must use "greater than" sign instead "greater than or equal" sign.
SELECT ROUND(AVG(`population`),0) AS population, DATE(`time`) AS date
FROM `swg_servertracker`
WHERE `time` > NOW() - INTERVAL 7 DAY
GROUP BY DATE(`time`)
ASC
LIMIT 7
You can get the result-set in a Derived table, and do a sorting on the results again.
Note that, in MySQL, Aliases defined in the Select clause can be used in Group By, Having and Order By clauses. So, I have aliased DATE(time) to avoid re-computation in Group by and Order By.
You can do this instead:
SELECT dt.population,
dt.date1 AS date
FROM (
SELECT ROUND(AVG(`population`),0) AS population,
DATE(`time`) AS date1
FROM `swg_servertracker`
WHERE `time` >= DATE(NOW()) - INTERVAL 7 DAY
GROUP BY date1
ORDER BY date1 DESC
LIMIT 7
) AS dt
ORDER BY dt.date1 ASC

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))

MYSQL order by datetime

I need to select one row with the "highest" date and time from a table, but I can't get the highest one, the ORDER BY DESC doesn't work.
Here's my query:
SELECT count(*) as c,
start,
UNIX_TIMESTAMP(start) as S,
duration
FROM appuntamento
WHERE DATE(start) = DATE('2014-04-08 18:30:00')
ORDER BY S DESC
LIMIT 1
I don't care about getting the start value in unix timestamp, it was the nth try to get through this
Any tips?
A few problems here. First, the presence of COUNT(*) turns this into an aggregate query, which do you not want. That's probably the cause of your trouble.
Second, if you have a lot of rows in your appuntamento table the performance of this query will be bad, because you can't use an index.
Presuming that you want the time and duration of the last (latest-in-time) row from a particular day in your table, and the number of appointments for that same day, you need to do this:
SELECT a.start, a.duration, b.count
FROM (
SELECT start,
duration
FROM appuntamento
WHERE start >= DATE('2014-04-08 18:30:00')
AND start < DATE('2014-04-08 18:30:00') + INTERVAL 1 DAY
ORDER BY start DESC, duration DESC
LIMIT 1
) AS a
JOIN (
SELECT COUNT(*) AS count
FROM appuntamento
WHERE start >= DATE('2014-04-08 18:30:00')
AND start < DATE('2014-04-08 18:30:00') + INTERVAL 1 DAY
) AS b
Explanation: First, this form of searching on start allows you to use an index on the start column. You want that for performance reasons.
WHERE start >= DATE('2014-04-08 18:30:00')
AND start < DATE('2014-04-08 18:30:00') + INTERVAL 1 DAY
Second, you need to handle the COUNT(*) as a separate subquery. I have done that.
Third, you can definitely do ORDER BY start DESC and it will work if start is a DATETIME column. No need for UNIX_TIMESTAMP().
Fourth, I used ORDER BY start DESC, duration DESC to arrange to return the longest appointment if there happen to be several with the same start time.
if all you want is one row returned then use the MAX() function without the order. should do the trick.
SELECT count(*) as c,
MAX(start) as highest_date,
UNIX_TIMESTAMP(start) as S,
duration
FROM appuntamento
WHERE DATE(start) = DATE('2014-04-08 18:30:00')
also with your order by statement. you need to add a group by so that you aren't combining incorrect rows with the COUNT() aggregate.
SELECT count(*) as c,
start,
UNIX_TIMESTAMP(start) as S,
duration
FROM appuntamento
WHERE DATE(start) = DATE('2014-04-08 18:30:00')
GROUP BY S
ORDER BY S DESC
LIMIT 1