How to use cases on order by in Mysql? - mysql

If t_date(column_name) is Today's date then
select * from `schedules`
ORDER BY available_seats <= 0 , STR_TO_DATE(departure_time,'%h:%i%p');
Else
select * from `schedules`
ORDER BY (available_seats <= 0 && (STR_TO_DATE(departure_time,'%h:%i%p') >= TIME(NOW()))), (STR_TO_DATE(departure_time,'%h:%i%p') <= TIME(NOW())), STR_TO_DATE(departure_time,'%h:%i%p');
END
Query 1 is for t_date = DATE(now())
Query 2 is for t_date != DATE(now())
How can i make it in a single query with condition on order by??

You can use CASE like below
SELECT *
FROM `schedules`
ORDER BY
available_seats <= 0 ,
CASE WHEN t_date <> CURRENT_DATE() AND (available_seats <= 0 && (STR_TO_DATE(departure_time,'%h:%i%p') >= TIME(NOW())))
THEN
(STR_TO_DATE(departure_time,'%h:%i%p') <= TIME(NOW()))
WHEN t_date = CURRENT_DATE()
THEN STR_TO_DATE(departure_time,'%h:%i%p')
ELSE STR_TO_DATE(departure_time,'%h:%i%p') END,
STR_TO_DATE(departure_time,'%h:%i%p')

In English, what are you actually trying to get your order by and maybe adjust your question for clarification...
However, I think you might want all "available_seats" values less or equal to zero FIRST, THEN based on the date from today. If that is the case, you may want something like..
order by
case when available_seats <= 0 then 1 else 2 end,
STR_TO_DATE(departure_time,'%h:%i%p')
But it should all be possible in a simple single query, but there is no context to what the seats, dates are for and what you want and why... Are you looking for something like "Sold-out" events sorted to top of list, then based on date of event with closest coming event listed first?
The case/when I have above basically puts any returned records that have available seats <= 0 in the first order sequence regardless of actual 0 or negative value... Then, anything else, if 1 seat or 1000 seats left are sorted after. The SECOND part of the order by is just on the date_time field itself. Since the order by is regardless of the "formatted" column that might be retrieved in the field list, I am just ordering by the date/based conversion as you had.

Related

Is it possible to make a SQL statement, based on a single column?

I'm struggling to write down an SQL statement for a query.
Basically, I want to select a record, based on a field, if this field is bigger than 0, then it should check if 1 hour is passed from a datetime field, if the field is lower or equal to 0 then it should ignore that check and just select it
In C# it would be something like this:
if (columnA > 0)
{
//select if (columnB < DATE_SUB(CURRENT_TIMESTAMP, INTERVAL 1 HOUR)))
//if it's not match, move on to next record
} else {
//just select it
}
So, in SQL what would be? Something like this:
SELECT field1, field2
FROM table
WHERE id = 1
AND intLv < fieldLv
AND IF (limitedLogin > 0) { lastLogin < DATE_SUB(CURRENT_TIMESTAMP, INTERVAL time HOUR) AND logins < totLogins }
ORDER BY id DESC LIMIT 1
I'm not expert on SQL/MySQL statements, so I don't know if that is entirely possible. Could someone enlighten me?
Basically, I want to select a record, based on a field, if this field is bigger than 0, then it should check if 1 hour is passed from a datetime field, if the field is lower or equal to 0 then it should ignore that check and just select it
You seem to be describing logic like this:
where columnA < 0 or
columnB < now() - interval 1 hour;
Note: This assumes that = 0 is the same as > 0.
The first condition returns all rows where columnA < 0. The second returns rows where column b is more than one hour ago.

SQL ORDER conditionally by column plus secondary column

I need to do an sql order by which covers a few cases...
I first need to order by a column expiry (epoch string), where this is greater than now or it is empty (non-expiring).
So expired records (those where expiry is less than now) will always come after the non-expired records.
Then within each of these cases, there needs to be a secondary search which is alphabetical.
tTABLE
id
name
expiry
active
My attempt so far:
select * from `tTABLE` WHERE `active`='1'
ORDER BY
CASE WHEN `expiry` > 1571410101 THEN 1
CASE WHEN `expiry` = '' THEN 2 ELSE 3 END,`expiry`
DESC
I think you want:
SELECT t.*
FROM `tTABLE` t
WHERE active = 1
ORDER BY (CASE WHEN expiry > unixtime()
THEN 1 -- explicit future expires first
WHEN expiry IS NULL
THEN 2 -- no expires second
ELSE 3
END),
name;
I'm not sure how this will work in MySql, but in Oracle the following SQL works:
SELECT *,
CASE WHEN expiry > 1571410101 THEN 1
WHEN expiry = '' THEN 2
ELSE 3
END exp
FROM tTABLE
WHERE active = '1'
ORDER BY exp DESC
I think the same will also work for MySql.

MySQL order by launch date within 3 months from now first, then launch date desc

I have got a table like the following. The select order needs to show closest launch date from now first(it is more urgent), then launch date desc. Say current date is 17-11-2017
tblProduct
01-02-2016
09-05-2015
03-11-2017
30-11-2017
02-01-2018
09-06-2018
The output order should be
30-11-2017
02-01-2018
09-06-2018
03-11-2017
09-05-2015
01-02-2016
The top 2 are within 3 months from now, so are shown first(between the 2 results, 30-11-2017 is closer from now so shown first).
What's the mysql query?
I'm sure not the most elegant way, but this could help you
http://sqlfiddle.com/#!9/2a6eca/1
SELECT *, IF(mydate BETWEEN NOW() AND DATE_ADD( NOW() , INTERVAL +3 MONTH) , 1, 0) as `dateInRange`
FROM `demo`
ORDER BY `dateInRange` DESC,
CASE WHEN `dateInRange` = 1 THEN `mydate` END ASC,
CASE WHEN `dateInRange` = 0 THEN `mydate` END DESC
What this does is adds another column dateInRange to the selection (and sets it to 1 if date is between now and 3 monts from now.
Then in the order by part we first sort by this column (making sure all the dates that are in the 3 months range are on the top), and when this column is equal to 0, then we sort by the actual date ascending. If this column is 0 we sort by dates descending.
This code does what you would like, but there must be a more elegant way to go about this.

My join sql query won't bring results

What could be wrong with my sql query here , I'd like to retrieve data from both tables meeting a WHERE condition
SELECT *, UNIX_TIMESTAMP(i.sent_date) AS udate
FROM ibc_sent_history as i INNER JOIN
ibc_messages as u
ON i.msg_ids = u.id
WHERE (i.sent_date >= '02-02-2013' AND i.sent_date <= '02-02-2014')
ORDER BY i.sent_date
LIMIT 200
Assuming your ibc_sent_history.sent_date datatype is DATETIME, here's a way to refactor this query. (This will work even if the datatype is DATE). You need to change your date input string format from 02-02-2013 to the more standard '2014-02-02` (YYYY-MM-DD).
SELECT whatever, whatever
FROM ibc_sent_history AS i
INNER JOIN ibc_messages AS u ON i.msg_ids = u.id
WHERE i.sent_date >= '2013-02-02'
AND i.sent_date < '2014-02-02' + INTERVAL 1 DAY
ORDER BY i.sent_date DESC
LIMIT 200
I changed the ORDER BY to include DESC. This is to return the most recent items, not the oldest. If that's not what you need, take off the DESC.
I changed the date formatting.
I changed the end of your selection range to
i.sent_date < '2014-02-02` + INTERVAL 1 DAY
That's because
i.sent_date <= '2014-02-02`
will include items that occur precisely at midnight on 2-Feb-2014, but won't include any other items on that day. What you probably want are items that occurred up to but NOT including midnight on the next day.
I don't know MySQL very well, but in SQL Fiddle when I run:
CAST('2014-02-02' AS DATE)
I get a date, when I run
CAST('02-02-2014' AS DATE)
I get NULL, so seems like your date format is wrong.
Demo: SQL Fiddle

Execute a subquery in MySQL that returns multiple rows?

I am executing a query that obviously contains a subquery in MySQL.
Let me just jump into the code:
SELECT DATEDIFF(CURDATE(),
(SELECT due FROM checkOut JOIN People ON checkOut.p_id = People.p_id
WHERE CASE WHEN DATE_SUB(date_add(CURDATE(),INTERVAL 4 MONTH), INTERVAL 3 MONTH)
>= checkOut.checkTime THEN 1 ELSE 0 END ORDER BY checkOut.due)
);
The main query is the SELECT DATEDIFF(). Within that is my subquery which essentially searches through the table to look for items that are overdue based on an interval. I know that there will be multiple rows returned from the query and that it will not work with how I currently have it set up.
What I want are multiple values to be returned from my SELECT DATEDIFF(), so that I can loop through it with php later. To elaborate, I want each of the rows returned in the subquery to have an associated value from DATEDIFF(). How can I modify this query to do what I want? Or if anyone has a better method, please let me know.
Any help is appreciated.
In case you are wondering the why there is a DATE_ADD() within the DATE_SUB(), it is to simply make the query work for today.
get rid of the subquery, you can calculate the difference directly.
SELECT DATEDIFF(CURDATE(), due), due
FROM checkOut JOIN People
ON checkOut.p_id = People.p_id
WHERE CASE
WHEN DATE_SUB(date_add(CURDATE(),INTERVAL 4 MONTH), INTERVAL 3 MONTH)
>= checkOut.checkTime
THEN 1
ELSE 0
END
ORDER BY checkOut.due
Use the subquery as table. e.g. below:
SELECT DATEDIFF(CURDATE(), d.due)
FROM
(SELECT due FROM checkOut JOIN People ON checkOut.p_id = People.p_id
WHERE CASE WHEN DATE_SUB(date_add(CURDATE(),INTERVAL 4 MONTH), INTERVAL 3 MONTH)
>= checkOut.checkTime THEN 1 ELSE 0 END ORDER BY checkOut.due)
) AS d;