ORDER BY based on multiple WHERE cases, is this possible? - mysql

Events can be a 1 day event or be an on-going event. This means that sometimes events can go for multiple days, weeks, or months.
As it is now, it is possible to sort the query result by END in ascending order (those expiring earlier shows first) or START in ASC (events based on start date). However, in both cases I have limitations that I am trying to reduce as much as I can.
When sorting by END, sometimes events that are ongoing and have already started get pushed to later in the list.
When sorting by START, events that have already started and are ongoing end up taking up the first sections of the list.
Is it possible to chain multiple ORDER BY statements based on logic rather than columns?
For example:
Get events that are expiring within the next 7 days:
SELECT * FROM data WHERE end < NOW() + INTERVAL 7 DAY;
Get events that are still ongoing between 7 days from today and ending within 14 days:
SELECT * FROM data WHERE NOW() + INTERVAL 7 DAY >= start AND end < NOW() + INTERVAL 14 DAY;
Get all remaining events...
SELECT * FROM data WHERE end >= NOW() + INTERVAL 14 DAY;
Basically, is it possible to join these into one query?
SELECT * FROM data
ORDER BY (logic 1), (logic 2), (logic 3);
Alternatively, I did get it working with running 3 separate queries and building up the result array on the server-side, but would like to simplify my code if possible.
Hoping that an end result will always show a list of events that will be expiring within 7 days first, then events that are happening between 7 - 14 days (could be starting or ongoing), then events that are still ongoing or starting after 14 days from today.

Depending on your SQL database, you can do something like this:
SELECT * FROM data
WHERE (end < NOW() + INTERVAL 7 DAY) -- logic 1
or (NOW() + INTERVAL 7 DAY >= start AND end < NOW() + INTERVAL 14 DAY) -- logic 2
or (NOW() + INTERVAL 7 DAY >= start AND end < NOW() + INTERVAL 14 DAY) -- logic 3
order by
case
when (end < NOW() + INTERVAL 7 DAY) then 1
when (NOW() + INTERVAL 7 DAY >= start AND end < NOW() + INTERVAL 14 DAY) then 2
when (NOW() + INTERVAL 7 DAY >= start AND end < NOW() + INTERVAL 14 DAY) then 3
else 4
end asc
;
You may also use union all:
SELECT 1 as sort_order, * FROM data
WHERE (end < NOW() + INTERVAL 7 DAY)
union all
SELECT 2 as sort_order, * FROM data
WHERE (NOW() + INTERVAL 7 DAY >= start AND end < NOW() + INTERVAL 14 DAY)
union all
SELECT 3 as sort_order, * FROM data
where NOW() + INTERVAL 7 DAY >= start AND end < NOW() + INTERVAL 14 DAY
The sort_order is probably not needed, but if you get your result not in the order of select, you may then use a subquery; also your database might forbids you from using order by in an union all.
select *
from (
SELECT 1 as sort_order, * FROM data
WHERE (end < NOW() + INTERVAL 7 DAY)
union all
SELECT 2 as sort_order, * FROM data
WHERE (NOW() + INTERVAL 7 DAY >= start AND end < NOW() + INTERVAL 14 DAY)
union all
SELECT 3 as sort_order, * FROM data
where NOW() + INTERVAL 7 DAY >= start AND end < NOW() + INTERVAL 14 DAY
) order by sort_order asc -- and any other key
I would personally go for the union all if possible (it is more readable).

You could try using a UNION where you select the data set and have a column that has the order you want. e.g.
SELECT 1 as orderby,* FROM data WHERE end < NOW() + INTERVAL 7 DAY;
UNION ALL
SELECT 2, * FROM data WHERE NOW() + INTERVAL 7 DAY >= start AND end < NOW() + INTERVAL 14 DAY
UNION ALL
SELECT 3, * FROM data WHERE end >= NOW() + INTERVAL 14 DAY
ORDER BY orderby, end
P.S. I would suggest you don't use SQL Keywords such as end for column names in your database, that can sometimes cause issues, enddate would be a better column name.
P.P.S. Avoid doing SELECT *, it is better to explicitly list the columns that you want.

I assume you are using MySQL.
use the case when .. then .. end in the select clause, then order by this column.
select *, case
when end < NOW() + INTERVAL 7 DAY then 1
when NOW() + INTERVAL 7 DAY >= start AND end < NOW() + INTERVAL 14 DAY then 2
else 3 as priority
from data
order by priority
Also, you can use the case in the order by clause.
Note: I didn't take care of your business logic, so test it well, just giving you how you can achieve it, hope it helps.

Related

how to get next three business days by mysql (current data and next three days)

I want to get the three working days from the current date as excluding Saturday and Sunday. can any one help me out here.
I have tried the interval method and DayOfWeek(day) <> 1 AND DayOfWeek(day) <> 7 but it is not giving me the proper result
Not very elegant but
select d
from
(
select curdate() as d
union all
select curdate() + interval 1 day
union all
select curdate() + interval 2 day
union all
select curdate() + interval 3 day
union all
select curdate() + interval 4 day
) tmp
where dayofweek(d) not in (1,7)
order by d
limit 3

Calculate momentum as weighted average by recency

I have a subscriptions table with an associated feed_id and creation timestamp. A feed has N subscriptions.
It's easy enough to show the most popular feeds using a group query to count the number of records with each feed_id. But I want to calculate momentum to show most trending feeds.
A simplified algorithm would be:
momentum of feed_id =
10 * (count of subscriptions with created_at in past day)
+ 5 * (count of subscriptions with created_at from 2-7 days ago)
+ 1 * (count of subscriptions with created_at from 7-28 days ago)
How can something like this be done in a single (My)SQL query instead of doing it with 3 queries and programmatically summing the results?
You can use conditional aggregation for this. MySQL treats booleans as integers, with true being "1", so you can just sum the expression for time.
I am guessing it looks something like this:
select feedid,
(10 * sum(createdat >= date_sub(now(), interval 1 day)) +
5 * sum(createdat >= date_sub(now(), interval 7 day) and
createdat < date_sub(now(), interval 1 day)) +
1 * sum(createdat >= date_sub(now(), interval 28 day) and
createdat < date_sub(now(), interval 7 day))
) as momentum
from subscriptions
group by feedid
SELECT 10*COUNT(IF(created_at >= CURDATE(), 1, 0)) +
5*COUNT(IF(created_at BETWEEN DATE_ADD(CURDATE(), - INTERVAL 7 days) AND DATE_ADD(CURDATE(), - INTERVAL 1 day), 1, 0) +
1*COUNT(IF(created_at BETWEEN DATE_ADD(CURDATE(), - INTERVAL 28 days) AND DATE_ADD(CURDATE(), - INTERVAL 8 day), 1, 0)
FROM ...
I'm not 100% sure I've caught the edge conditions (yesterday or 8 days ago) to get exactly the right count. You'll want to test that.
If you're interested in 24-hour periods then just substitute NOW() for CURDATE() and everything will go to DATETIME.

return records between yesterday and last 7 days + mysql

here is the code I am using to return past 24 hours records
SELECT *
FROM mytable
WHERE CASE WHEN `created` > DATE_SUB(NOW(), INTERVAL 1 DAY) THEN 1 ELSE 0 END
how to return records between yesterday and last 7 days
Use the BETWEEN operator.
CASE WHEN created BETWEEN DATE_SUB(NOW(), INTERVAL 7 DAY) AND DATE_SUB(NOW(), INTERVAL 1 DAY)
THEN 1
ELSE 0
END
Try this, it works without case statement, so should be faster:
SELECT *
FROM mytable
WHERE created BETWEEN date(CURRENT_TIMESTAMP-7) AND date(CURRENT_TIMESTAMP-1);

MySQL select all dates that are an increment of x days

Is it possible to query for all dates in the future that are an increment of x days?
i.e.
SELECT *
FROM bookings
WHERE date >= CURDATE()
AND
(
date = CURDATE() + INTERVAL 6 DAY
OR date = CURDATE() + INTERVAL 12 DAY
OR date = CURDATE() + INTERVAL 18 DAY
etc.
)
Something like:
SELECT
*
FROM table
WHERE
date >= CURDATE()
AND
DATEDIFF(CURDATE(), date) % 6 = 0
Datediff returns the number of days difference, and % 6 says return the remainder when divided by six.
Yes.
Your logic is flawed, though. You probably meant
SELECT *
FROM table
WHERE
date = CURDATE() + INTERVAL 6 DAY
OR date = CURDATE() + INTERVAL 12 DAY
OR date = CURDATE() + INTERVAL 18 DAY
And don't use table names like "table" and field names like "date" (i.e. reserved words).

Count rows in mysql database where timestamp within X interval

I am trying to count the rows in a table in my database that were inserted within X hours or X days. I have tried repeatedly but I keep getting empty set responses.
The start_stamp column in my table is formatted like this: 2013-08-07 18:18:37
I have tried many variations on:
select * from mytable where start_stamp = now() interval -1 day;
But all to no avail. Can someone tell me what I am doing wrong?
Rather than selecting rows where start_stamp is equal to now() - 1day, you need rows where it is greater than or equal to that range. Additionally, your syntax is a little off. MySQL's date arithmetic uses column_value - INTERVAL <number> <period>, so you need:
SELECT COUNT(*) AS num_new_rows
FROM mytable
WHERE start_stamp >= NOW() - INTERVAL 1 DAY
Likewise to get n hours ago, use INTERVAL n HOUR
# Within 3 hours...
WHERE start_stamp >= NOW() - INTERVAL 3 HOUR
The syntax for date interval arithmetic is described in a small paragraph beneath the DATE_ADD() function reference in the official MySQL documentation.
Here are a some examples...
All entries within the past 6 hours:
select * from mytable where start_stamp >= now() - interval 6 hour;
All entries within the past 2 days:
select * from mytable where start_stamp >= now() - interval 2 day;
All entries within the past 2 1/2 days:
select * from mytable where start_stamp >= now() - interval 2 day - interval 12 hour;