The following code is producing a list with the non-expired rows on top, then the ones with unknown expiry date and at the end the already expired (all of them in ascending order). The problem is that I want the last block of already expired rows to be in descending order so it displays the rows that expired more recently on top of that block without altering the order of the other top blocks.
Basically, I am trying to find a way to incorporate two "ORDER BY" clauses within the same recordset...
Any ideas? Thanks
SELECT *
FROM prueba
WHERE UPPER(CONCAT(Company,Deal,keywords,Type,Expiry,Name)) LIKE UPPER(%s)
ORDER BY (CASE
WHEN prueba.Expiry = 'UNKNOWN' THEN 1
WHEN prueba.Expiry < CURRENT_DATE THEN 2
END)
, prueba.Expiry ASC
Try this
DEMO FIDDLE
SELECT * FROM t
order by case
when expiry = 'Unknown' Then 1
WHEN expiry >= CURRENT_DATE THEN 0
ELSE 2 END,
CASE WHEN expiry >= CURRENT_DATE THEN expiry END,
CASE WHEN expiry < CURRENT_DATE THEN expiry END desc
Separate those records that have expired into another SELECT clause, then UNION ALL:
(SELECT *
FROM prueba
WHERE UPPER(CONCAT(Company,Deal,keywords,Type,Expiry,Name)) LIKE UPPER(%s)
AND prueba.Expiry > CURRENT_DATE
ORDER BY prueba.Expiry DESC)
UNION ALL
(SELECT *
FROM prueba
WHERE UPPER(CONCAT(Company,Deal,keywords,Type,Expiry,Name)) LIKE UPPER(%s)
AND prueba.Expiry = 'UNKNOWN')
UNION ALL
(SELECT *
FROM prueba
WHERE UPPER(CONCAT(Company,Deal,keywords,Type,Expiry,Name)) LIKE UPPER(%s)
AND prueba.Expiry < CURRENT_DATE
ORDER BY prueba.Expiry DESC)
Related
I have a query in MariaDB 10.3 database where there is a field called "expiration_date" that stores a unix timestamp, but if there is no data in the field the default is set to "0".
I'm trying to use a WHERE clause to check the current date against the expiration_date to filter out any records that are past the expiration_date. Below is what I have.
SELECT entry_id, title, (CASE WHEN expiration_date = "0" THEN CURDATE() + INTERVAL 1 DAY ELSE FROM_UNIXTIME(expiration_date, "%Y-%m-%d") END) AS expiration_date
FROM channel_titles
WHERE CURDATE() < expiration_date
This returns and empty result set... what am I missing?
There's a very simple solution to this and it only requires you to change two things from your original query:
The first part is your column (CASE expression) alias - you should define your alias with something not similar to any of the column names present in the table. From your query, you have a column expiration_datein your table and you also set an alias for your CASE expression with expiration_date as well and since you're using WHERE, the query will definitely do the lookup based on your table expiration_date column instead of your CASE expression. Rename that alias to something like exp_date... but doing WHERE exp_date ... will return you an error. Refer to the second point below.
The second part is your WHERE - since you're doing lookup from a CASE expression (or perhaps custom generated value/column) with newly assigned alias of exp_date, you can't use it in WHERE.. well, unless you make the query as a subquery/derived table then do the WHERE outside.. but you don't need to. You only need to change WHERE to HAVING and you should be able to use the exp_date and get your result.
So, with those two changes, your query should be something like this:
SELECT entry_id, title,
(CASE WHEN expiration_date = "0" THEN CURDATE() + INTERVAL 1 DAY ELSE
FROM_UNIXTIME(expiration_date, "%Y-%m-%d") END) AS exp_date
FROM channel_titles
HAVING CURDATE() < exp_date;
demo fiddle
You're trying to use an alias of expiration_date from your CASE statement in your WHERE clause.
Two problems with this:
You cannot use column aliases in the WHERE clause. Refer to this post here.
WHERE happens before SELECT in the execution chain.
Your alias matches an actual column name in your table, so your
WHERE clause is not throwing an error regarding your alias, its
comparing the current date to the expiration_date column in the table,
thus, throwing off your expected result.
Solutions:
If you want to use the alias in your WHERE clause, there are a few options for you to force SQL to handle the SELECT before the WHERE clause.
You can use a subquery (or subselect) to force logical order of
operation by using parentheses:
SELECT
a.entry_id,
a.title,
a.expiration_date
FROM
(SELECT
entry_id,
title,
(CASE WHEN expiration_date = 0 THEN CURDATE() + INTERVAL 1 DAY ELSE FROM_UNIXTIME(expiration_date, '%Y-%m-%d') END) AS expiration_date
FROM channel_titles
) a
WHERE CURDATE() < a.expiration_date
You can declare your alias in a Common Table Expression (CTE), then SELECT it FROM the CTE:
WITH cte AS (SELECT
entry_id,
title,
(CASE WHEN expiration_date = 0 THEN CURDATE() + INTERVAL 1 DAY ELSE FROM_UNIXTIME(expiration_date, '%Y-%m-%d') END) AS expiration_date
FROM channel_titles)
SELECT
entry_id,
title,
expiration_date
FROM cte
WHERE CURDATE() < expiration_date
You can disregard using your alias entirely in your WHERE clause and plug in the logic from your SELECT statement directly into your WHERE clause. However, this may appear redundant from a readability perspective; also, extra processing should be considered when using this approach as well, but if you have a small data set this method will work just fine:
SELECT
entry_id,
title,
(CASE WHEN expiration_date = 0 THEN CURDATE() + INTERVAL 1 DAY ELSE FROM_UNIXTIME(expiration_date, '%Y-%m-%d') END) AS expiration_date
FROM channel_titles
WHERE CURDATE() < (CASE WHEN expiration_date = 0 THEN CURDATE() + INTERVAL 1 DAY ELSE FROM_UNIXTIME(expiration_date, '%Y-%m-%d') END)
Input:
entry_id
title
expiration_date
expiration_date_date
1
test1
1695513600
2023-09-24
2
test2
0
2022-09-15
3
test3
1662768000
2022-09-10
Output:
entry_id
title
expiration_date
1
test1
2023-09-24
2
test2
2022-09-15
db<>fiddle here.
I have a table with some events like this
id----------title-----------date-------------status
1-----------birthday-------2018-03-12--------1
2-----------match----------2018-03-13--------2
3-----------anniversary----2018-03-10--------1
4-----------trip-----------2018-03-15--------1
5-----------birthday-------2018-03-17--------2
6-----------birthday-------2018-03-11--------1
Expected Result
id----------title-----------date-------------status
1-----------birthday-------2018-03-12--------1
4-----------trip-----------2018-03-15--------1
5-----------birthday-------2018-03-17--------2
2-----------match----------2018-03-13--------2
6-----------birthday-------2018-03-11--------1
3-----------anniversary----2018-03-10--------1
I need to query it like the first rows which have dates greater than today with status 1 should appear first and then the rest in desc.
Suppose today is 2018-03-11 then row with id 1 should appear first and then the rest of the rows is desc order
This is what I have tried so far
SELECT *
FROM events
ORDER BY (date > CURDATE() and status = 1) asc,
date desc
You can use multiple keys in an order by:
order by (date >= curdate() and status = 1) desc,
date desc
I believe your SQL should be something like this but is hard to say without expected results.
Query
SELECT
*
FROM
[table]
WHERE
date > CURDATE()
AND
status = 1
ORDER BY
date ASC
LIMIT 1
UNION
SELECT
*
FROM
[table]
WHERE
id NOT IN (
SELECT
id
FROM
[table]
WHERE
date > CURDATE()
AND
status = 1
LIMIT 1
)
AND
date > CURDATE()
ORDER BY
date DESC
I currently have a query that finds all rows (with status=0) that have occurred before now:
SELECT id, COUNT(1) FROM tbl WHERE status = 0 AND date < UNIX_TIMESTAMP() GROUP BY id;
However, now I'd also like to be able to retrieve the values on the other side of this--i.e., I want to get all dates available after and before now, as two distinct values.
Is there any way to optimize this besides simply running two separate queries?
SELECT id
, SUM(date < UNIX_TIMESTAMP()) AS BeforeNow
, SUM(date > UNIX_TIMESTAMP()) AS AfterNow
FROM tbl
WHERE status = 0
GROUP BY id;
date < UNIX_TIMESTAMP() is a boolean expression, which equates to 1 or 0. The SUM of the expression is equal to the amount of times it was true, or its count.
You can do a conditional count.
SELECT id,
COUNT(CASE WHEN date < UNIX_TIMESTAMP() THEN 1 ELSE null END ) ,
COUNT(CASE WHEN date > UNIX_TIMESTAMP() THEN 1 ELSE null END )
FROM tbl GROUP BY id
I have a table structure that looks like this:
I have a perfectly working query that counts how many records there have been per day the last 30 days. It looks likes this:
SELECT DATE(timestamp) AS date, COUNT(id) AS emails FROM 'emails WHERE timestamp >= now() - interval 1 month GROUP BY DATE(timestamp)
This outputs the following which is perfectly fine:
However, the next thing seems too difficult for me to imagine. Now I want to count how many records there have been per day the last 30 days BUT only where newsletter = 1.
I've tried to put a WHERE statement looking like this:
SELECT DATE(timestamp) AS date, COUNT(*) AS emails, nyhedsbrev FROM emails WHERE timestamp >= now() - interval 1 month AND nyhedsbrev = 1 GROUP BY DATE(timestamp)
... And that outputs the following:
The problem is, that its omitting the records with newsletter = 0 and there by I cant compare my first query against the new one, as the dates doesnt match. I know that is because I use WHERE newsletter = 1.
In stead of omitting the record I want a query that just puts a "0" from that date. How can I do this? The final query should be outputting this:
You should be able to simply use SUM() and IF() to get the desired output:
SELECT
DATE(timestamp) AS date,
COUNT(*) AS emails,
SUM(IF(nyhedsbrev > 0, 1, 0)) as nyhedsbrev_count
FROM
emails
WHERE
timestamp >= now() - interval 1 month
GROUP BY
DATE(timestamp)
SQLFiddle DEMO
Edit: You might even be able to simplify it, since it's a boolean, and simply use SUM(nyhedsbrev), but this REQUIRES that nyhedsbrev is only 0 or 1:
SELECT
DATE(timestamp) AS date,
COUNT(*) AS emails,
SUM(nyhedsbrev) as nyhedsbrev_count
FROM
emails
WHERE
timestamp >= now() - interval 1 month
GROUP BY
DATE(timestamp)
Possibly best to get a list of the dates and then left join that against sub queries to get the counts you require.
Something like this
SELECT Sub1.date, Sub2.emails, IFNULL(Sub3.emails, 0)
FROM (SELECT DISTINCT DATE(timestamp) AS date
FROM emails
WHERE timestamp >= now() - interval 1 month) Sub1
LEFT OUTER JOIN (SELECT DATE(timestamp) AS date, COUNT(id) AS emails
FROM emails WHERE timestamp >= now() - interval 1 month
GROUP BY DATE(timestamp)) Sub2
ON Sub2.date = Sub3.date
LEFT OUTER JOIN (SELECT DATE(timestamp) AS date, COUNT(*) AS emails
FROM emails
WHERE timestamp >= now() - interval 1 month AND nyhedsbrev = 1
GROUP BY DATE(timestamp)) Sub3
ON Sub1.date = Sub3.date
(you can probably optimise one subselect of this away, but I have done it in full to make it obvious how it is working)
Assuming newsletter is boolean 1/0 values then this might give you the table that you want:
SELECT DATE(timestamp) AS date, COUNT(*) AS emails, nyhedsbrev
FROM emails WHERE timestamp >= now() - interval 1 month GROUP BY DATE(timestamp),nyhedsbrev ;
Just adding another GROUP BY parameter.
Inside of my Auctions table, I have a column called Auction_StartDate. The value of a row is like this: 2012-10-27 13:45:30.
I need a query that will return the next closest date and time after that. So if the next Auction_StartDate is 2012-10-27 18:30:00, it should return that before the date turns to 2012-10-28.
If you mean to do this for every row, try this:
SELECT a1.id,
(SELECT MIN(a2.Auction_StartDate)
FROM Auctions a2
WHERE a2.Auction_StartDate > a1.Auction_StartDate) AS nextStartDate
FROM Auctions a1
You can use MIN to find the closest value without using LIMIT and ORDER BY clause.
SELECT MIN(DATE(Auction_StartDate)) closestDate
FROM Auctions
WHERE DATE(Auction_StartDate) > '2012-10-27'
SQLfiddle Demo
May be this one helps
SELECT DATE(Auction_StartDate) closestDate
FROM Auctions
WHERE DATE(Auction_StartDate) > '2012-10-27'
order by Auction_StartDate ASC
limit 1
SELECT (case when Hour(StartDate)>=12 then DATE_ADD(StartDate,
INTERVAL 1 DAY) else StartDate end) as 'date' FROM table
------------------------------
pleaes add your column name where is static date :
est on : http://sqlfiddle.com/#!2/b8435/19
SELECT (case when Hour(StartDate )>=12 then
DATE_FORMAT( DATE_ADD(StartDate ,INTERVAL 1 DAY), '%Y-%m-%d')
else DATE_FORMAT(StartDate , '%Y-%m-%d') end) as 'date' from tabel