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?
CodePudding user response:
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
WHEREclause. Refer to this post here.
WHEREhappens beforeSELECTin the execution chain.
- Your alias matches an actual column name in your table, so your
WHEREclause is not throwing an error regarding your alias, its comparing the current date to theexpiration_datecolumn 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
SELECTitFROMtheCTE:
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
WHEREclause and plug in the logic from yourSELECTstatement directly into yourWHEREclause. 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.
CodePudding user response:
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 (
CASEexpression) 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 columnexpiration_datein your table and you also set an alias for yourCASEexpression withexpiration_dateas well and since you're usingWHERE, the query will definitely do the lookup based on your tableexpiration_datecolumn instead of yourCASEexpression. Rename that alias to something likeexp_date... but doingWHERE 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 aCASEexpression (or perhaps custom generated value/column) with newly assigned alias ofexp_date, you can't use it inWHERE.. well unless you make the query as a subquery then do theWHEREoutside.. but you don't need to. You only need to changeWHEREtoHAVINGand you should be able to use theexp_dateand 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;
