Is there any way to use CASE in WHERE clause in Oracle? - sql

I have the following data in DB which supposed to be a date but in number format:
date_col:
20200130.95656
20200910.85213
0
20220101.55412
0
0
20220730.85626
Now I need to format the dates to mm/dd/yyyy so I used the following command in oracle:
to_char(to_date(trunc(date_col),'yyyymmdd'),'mm/dd/yyyy')
to output it like this 07/30/22. But the problem I still need to get the 0 values in the date_col and at the same time to only get the dates between 09/10/2020 to 07/30/2022 so I was wondering if there is a way to do this in Oracle?
I can filter the dates by using the following code:
SELECT char(to_date(trunc(date_col),'yyyymmdd'),'mm/dd/yyyy')
FROM table1 WHERE
date_col != 0
AND char(to_date(trunc(date_col),'yyyymmdd'),'mm/dd/yyyy') BETWEEN '09/10/2020' AND '07/30/2022';
but I need to get the 0 at the same time. I'm thinking on using CASE on the WHERE clause but don't know how. I Googled but doesn't seems to answer my question. I'm only used to using CASE on the SELECT and not on WHERE so I'm wondering if there is a trick on using it on WHERE.
Update
Output should be:
data_col
________________
- 09/10/2020
- 0
- 01/01/2022
- 0
- 0
- 07/30/2022
Date is on number because of vendor who created the formatting of the date..

Before we get into this, there are a few things I need to point out here. I know you may not have much influence on the schema design, but you need to understand you are working with a BROKEN SCHEMA.
Building on this, it bears repeating internationalization/cultural issues mean converting these numbers to strings and then to dates is about the slowest and most error-prone way possible to accomplish the conversion to date values.
Finally, the BETWEEN '09/10/2020' AND '07/30/2022' comparison is horribly broken. The left-hand side casts back to char and so you are doing string comparisons instead of date comparisons. There is no value that will ever satisfy this condition, because when evaluating the boundary strings the 0s are equal, the 9 is already greater than the 7, and that's as far as you'll ever get.
That out of the way, you can use CASE in a WHERE clause. However, remember that CASE is not like an if() statement. You don't use it to select code branches. It is an expression... it produces a value. Therefore, to use CASE with a WHERE clause, you want something to compare with that value result. For example: WHERE 0 = CASE WHEN SomeColumn=1 THEN 0 ELSE 1 END
But we don't need CASE here. This will do the job:
SELECT char(to_date(trunc(date_col),'yyyymmdd'),'mm/dd/yyyy')
FROM table1
WHERE date_col = 0 OR date_col BETWEEN 20200910.0 AND 20220730.99999
This will perform much better, not only because it avoids the extra conversions, but in leaving the column values intact for the comparison it also preserves the ability to use indexes with the column, which cuts to the core of database performance.

You could use this. If your date_col have no decimal part you could use BETWEEN instead
SELECT TO_CHAR(TO_DATE(TRUNC(date_col),'yyyymmdd'),'mm/dd/yyyy')
FROM table1
WHERE date_col = 0
OR (date_col >= 20200910 AND date_col < 20200730);

Related

calculating duration (Date - Date) from a case Expression

I'm writing a complex query and I'm having some problems with calculating from case expression (thank you). Essentially, thanks to some great help on this site I managed to solve another issue by using an ordered table but now one of the select statements I was previously using no longer functions with the OT.
I get an error:
Conversion failed when converting date and/or time from character string
The core of the problem is that I want to calculate duration from subtracting a start date from an end date. The code I had previously used was
select
cast((CASE
WHEN ap.startLIKE '%[^0-9]%'
THEN CAST(ap.end as datetime)
ELSE NULL
END)
-
(CASE
WHEN ap.start LIKE '%[^0-9]%'
THEN CAST(ap.start as datetime)
ELSE NULL
END) as time) as Duration,
ap.start, ap.end, ap.description
from
Table as AP
group by
ap.start, ap.end, ap.description
but when I changed to draw this information from the ordered table as:
Select
ot.starttime as "Start Time",
ot.endtime as "End Time",
ot.description "Description",
ot.entrynumber as "ID Number",
cast((CASE
WHEN ot.endtime LIKE '%[^0-9]%'
THEN CAST(ot.endtime as datetime)
ELSE NULL
END)
-
(CASE
WHEN ot.starttime LIKE '%[^0-9]%'
THEN CAST(ot.starttime as datetime)
ELSE NULL
END) as time) as duration
From
OrderedTable as OT
group by
ot.starttime, ot.endtime, ot.description, ot.entrynumber
order by
ot.endtime
I get the error. Previously applying the group by clause had removed the error, but not from the ordered table.
Genuinely unsure what is causing the problem as I've been trying to eliminate the conversion to date issue constantly, I would appreciate any help.
The originating problem has been that the date fields on the server are in nvarchar format and not datetime, I've been trying to get around that.
If I understand your question correctly - you can use
TIMEDIFF(date1, date2)
or TIMESTAMPDIFF(SECOND, date2, date1);
where date2 < date1 to find the time between two dates. In the first case you will get the result in HH:mm:ss format and in the second case it will be in seconds - depends what you need. Since you didn't post an example how a date looks in your column I can't be sure, but I think you wont need conversions - you can substract your dates as chars (If they look this way '2016-12-31 11:02:00')
Since the column was formatted in nvarchar32 rather than date/time which it should have been, it resulted in periodically the database receiving a text value for (some) open records for the finish time rather than a NULL value.
This occurred only periodically and for whatever reason proposed by whoever designed the database.
This ideally shouldn't happen since its against database convention to do this kind of thing, as NULL values are simply ignored by calculation functions since they are unknown. Still unclear why the case expression didn't resolve the issue, but using the filter FinishTime <> "error text", I resolved the problem.

How to compare two date values in Oracle based on the date and time

I'm working in an aviation environment where I have to compare two values. The OEM in all its wisdom has stored all datetime values in char. The values to compare are called, say, aibt and sibt
As part of the where clause I need to check if aibt is greater than sibt. So this is what I did :
To_Date(aibt,'YYYYMMDDHH24MISS') > To_Date(sibt,'YYYYMMDDHH24MISS')
When applying to_date, the original char values are converted to : 25.12.2013 12:54:00
But I get the following error :
ORA-01841: (full) year must be between -4713 and +9999, and not be 0
You obviously have some data that can not be converted with TO_DATE to a proper date. Feel free to berate your OEM for not defining constraints or otherwise prevent this from happening.
While he gets around to fix his problems, you can use this as a workaround:
WHERE aibt > sibt
Seems like an error in your data. Try this first:
Select to_date(aibt,'YYYYMMDDHH24MISS') from your_table
Select to_date(sibt,'YYYYMMDDHH24MISS') from your_table
to check if you have some incorrect data in your columns.
When you identified your problematic data in your column, you could find the data that is messing this up with somethin like this:
select *
from yourtable
where not regexp_like(aibdt, '^[[:digit:]]+$')
OR length(aibdt)<4 OR
substr(aibdt,1,4)<0
There was some problem in the data. Also thanks Twinkles for your contribution. I hadnt thought of that. Can anyone explain how comparing two char values work?
I got the answer (what I'd really required) in another post Oracle: how to add minutes to a timestamp? which I used for constructing the comparison :
WHERE To_Date(aibt,'YYYYMMDDHH24MISS')>To_Date(sibt,'YYYYMMDDHH24MISS')+ INTERVAL '15' MINUTE
Thanks everyone!

Select data in date format

I have a query in which I want to select data from a column where the data is a date. The problem is that the data is a mix of text and dates.
This bit of SQL only returns the longest text field:
SELECT MAX(field_value)
Where the date does occur, it is always in the format xx/xx/xxxx
I'm trying to select the most recent date.
I'm using MS SQL.
Can anyone help?
Try this using ISDATE and CONVERT:
SELECT MAX(CONVERT(DateTime, MaybeDate))
FROM (
SELECT MaybeDate
FROM MyTable
WHERE ISDATE(MaybeDate) = 1) T
You could also use MAX(CAST(MaybeDate AS DateTime)). I got in the (maybe bad?) habit of using CONVERT years ago and have stuck with it.
To do this without a conversion error:
select max(case when isdate(col) = 1 then cast(col as date) end) -- or use convert()
from . . .
The SQL statement does not specify the order of operations. So, even including a where clause in a subquery will not guarantee that only dates get converted. In fact, the SQL Server optimizer is "smart" enough to do the conversion when the data is brought in and then do the filtering afterwards.
The only operation that guarantees sequencing of operations is the case statement, and there are even exceptions to that.
Another solution would be using PATINDEX in WHERE clause.
SELECT PATINDEX('[0-9][0-9]/[0-9][0-9]/[0-9][0-9][0-9][0-9]', field_value)
Problem with this approach is you really are not sure if something is date (e.g. 99/99/9999 is not date).
And problem with IS_DATE is it depends on configuration (e.g. DATEFORMAT).
So, use an appropriate option.

Date range comparison using varchar columns in Teradata

I've been tasked to take a calendar date range value from a form front-end and use it to, among other things, feed a query in a Teradata table that does not have a datetime column. Instead the date is aggregated from two varchar columns: one for year (CY = current year, LY = last year, LY-1, etc), and one for the date with format MonDD (like Jan13, Dec08, etc).
I'm using Coldfusion for the form and result page, so I have the ability to dynamically create the query, but I can't think of a good way to do it for all possible cases. Any ideas? Even year differences aside, I can't think of anything outside of a direct comparison on each day in the range with a potential ton of separate OR statements in the query. I'm light on SQL knowledge - maybe there's a better way to script it in the SQL itself using some sort of conversion on the two varchar columns to form an actual date range where date comparisons could then be made?
Here is some SQL that will take the VARCHAR date value and perform some basic manipulations on it to get you started:
SELECT CAST(CAST('Jan18'||TRIM(EXTRACT(YEAR FROM CURRENT_DATE)) AS CHAR(9)) AS DATE FORMAT 'MMMDDYYYY') AS BaseDate_
, CASE WHEN Col1 = 'CY'
THEN BaseDate_
WHEN Col1 = 'LY'
THEN ADD_MONTHS(BaseDate_, -12)
WHEN Col1 = 'LY-1'
THEN ADD_MONTHS(BaseDate_, -24)
ELSE BaseDate_
END AS DateModified_
FROM {MyDB}.{MyTable};
The EXTRACT() function allows you to take apart a DATE, TIME, or TIMESTAMP value.
You have you use TRIM() around the EXTRACT to get rid of the whitespace that is added converting the DATEPART to a CHAR data type. Teradata is funny with dates and often requires a double CAST() to get things sorted out.
The CASE statement simply takes the encoded values you suggested will be used and uses the ADD_MONTHS() function to manipulate the date. Dates are INTEGER in Teradata so you can also add INTEGER values to them to move the date by a whole day. Unlike Oracle, you can't add fractional values to manipulate the TIME portion of a TIMESTAMP. DATE != TIMESTAMP in Teradata.
Rob gave you an sql approach. Alternatively you can use ColdFusion to generate values for the columns you have. Something like this might work.
sampleDate = CreateDate(2010,4,12); // this simulates user input
if (year(sampleDate) is year(now())
col1Value = 'CY';
else if (year(now()) - year(sampleDate) is 1)
col1Value = 'LY'
else
col1Value = 'LY-' & DateDiff("yyyy", sampleDate, now());
col2Value = DateFormat(sampleDate, 'mmmdd');
Then you send col1Value and col2Value to your query as parameters.

XCODE - SQL Dates

I am trying to update my SQL DB when the the date has expired (i.e is less than now)
I have this lovely bit of SQL code
Update Notifications
SET Active = 'N'
where CAST(SetDate AS DATE) <= CAST('2012-08-23 11:19:00 +0000' AS DATE)
But it updates all the records (even if the date is not less than now)
I have also tried
Update Notifications
SET Active = 'N'
where CAST(SetDate AS DATE) < CAST('2012-08-23 11:19:00 +0000' AS DATE)
But this dosent affect any rows.
I guess I have something a little confused??
Any help???
Thanks
I cannot see anything odd about the code, except for this line:
CAST('2012-08-23 11:19:00 +0000' AS DATE)
If you are using CAST() to change it to the date, then there is no need to pass in the time portion of the value.
You did not provide the full table schema but one thing to consider is using a bit field for the Y/N values.
Here is a SQL Fiddle with the code working
Update Notifications
SET Active = 'N'
where CAST(SetDate AS DATE) <= CAST('2012-08-23' AS DATE)
One thing you have confused is types in SQL. I don't know what database you are using, but it probably has types for boolean (or int) and date. Storing booleans and dates as strings is very inefficient. For example, even if your example did work it would have to scan the entire database and cast each value in order to compare it.
The clue to the error is the < vs <=. This hints at the values being equal. If the cast fails on both sides, and returns the default value of zero, then then will be equal.
You should change your schema to use the correct types, then your query will work.