Case statement with date and string - sql

I have the following error, ORA-01841
(full) year must be between -4713 and +9999 and not be 0
The error is coming from the below case statement.
Any help on what's going on and how to fix?
SQL
SELECT
CASE
WHEN NVL(uap.us_pend_dt, act_d_dt) >= TO_CHAR(TO_DATE('".PENDING_DATE_CUTOFF."','YYYY-MM-DD'),'mm/dd/yyyy')
THEN NVL(uap.us_pend_dt, act_d_dt)
ELSE CASE WHEN NVL(act_d_dt, SYSDATE) <TO_CHAR(TO_DATE('".HIRE_DATE_CUTOFF."','YYYY-MM-DD'),'mm/dd/yyyy')
THEN act_d_dt
ELSE ua_dt
END
END AS h_DT

It looks like you are trying to convert a string ".PENDING_DATE_CUTOFF." to a date. I just did the following and got an identical error:
SELECT TO_DATE('".PENDING_DATE_CUTOFF."', 'YYYY-MM-DD') FROM dual;
Is .PENDING_DATE_CUTOFF. (with periods) the name of a column in your table? If so then omit the single quote characters, e.g.:
SELECT TO_DATE(".PENDING_DATE_CUTOFF.", 'YYYY-MM-DD') FROM dual;
Of course this will give an altogether different error [ORA-00904: ".PENDING_DATE_CUTOFF.": invalid identifier] if you run as is! So I think you might want something like the following (I'm assuming the other date columns are actual dates, and the cutoff columns are VARCHAR2 columns that are storing dates in the format YYYY-MM-DD:
SELECT CASE WHEN COALESCE(uap.us_pend_dt, act_d_dt) >= TO_DATE(".PENDING_DATE_CUTOFF.", 'YYYY-MM-DD') THEN COALESCE(uap.us_pend_dt, act_d_dt)
ELSE WHEN COALESCE(act_d_dt, SYSDATE) < (TO_DATE(".HIRE_DATE_CUTOFF.", 'YYYY-MM-DD') THEN act_d_dt
ELSE act_d_dt
END AS h_dT
FROM mytable;
Note that I also got rid of the extraneous CASE statement and converted the Oracle-specific NVL() function to the ANSI-standard COALESCE() function.
EDIT: In the event that your *_dt columns are strings and not dates, best to convert them to dates using TO_DATE() before comparing - that way you're comparing dates to dates.
Hope this helps.

Presumably, your date columns are stored as dates. So, do the comparison as dates not strings:
SELECT (CASE WHEN NVL(uap.us_pend_dt, act_d_dt) >= TO_DATE('".PENDING_DATE_CUTOFF."', 'YYYY-MM-DD')
THEN NVL(uap.us_pend_dt, act_d_dt)
WHEN NVL(act_d_dt, SYSDATE) < TO_DATE('".HIRE_DATE_CUTOFF."', 'YYYY-MM-DD')
THEN act_d_dt
ELSE ua_dt
END) AS h_DT
You also don't need the extra case expressions.

Related

Oracle SQL TO_DATE - two date formats

I am trying to update several dates to the end of the month. However, my table has two separate date formats ('DD-MON-YY' and 'YYYYMMDD'). How can I update both dates in an update statement? Also, I want the new dates to be in 'YYYYMMDD' format.
Update MY_TABLE
set MY_DATE = TO_CHAR(LAST_DAY(TO_DATE(MY_DATE,'DD-MON-YY')),'YYYYMMDD');
As David noted in the comments, the "real" solution here would be to add a date column and use that. If that's not an option, you could differentiate old data from new data based on the existence of the - character:
UPDATE my_table
SET my_date =
TO_CHAR(LAST_DAY(TO_DATE(my_date, CASE WHEN my_date LIKE '%-%'
THEN 'DD-MON-YY'
ELSE 'YYYYMMDD'
END)),
'YYYYMMDD')

Convert YYYY-MM-DD into YYYYMMDD and insert into Big query partitioned table

I am trying to perform date conversion of a column from YYYY-MM-DD to YYYYMMDD and insert the final value into a big query partitioned table.
I am getting error as "invalid value" after conversion.
I tried the below,
CASE WHEN o_date is not null THEN PARSE_DATE("%Y%m%d",cast(a.o_date as string))
ELSE PARSE_DATE("%Y%m%d",'19000101')
END as o_date
or
CASE WHEN o_date is not null THEN cast(FORMAT_DATE("%Y%m%d",o_date) as DATE)
ELSE PARSE_DATE("%Y%m%d",'19000101')
END as o_date
Could you please help to achieve the same
Cheers!
Instead of using PARSE_DATE you can use FORMAT_DATE. According to the documentation it formats a date_expression according to a format string, as follows:
FORMAT_DATE(format_string, date_expr)
Notice that the format_string must be a STRING and it should be written between double quotes following these formatting elements dictionary and the date_expr must be a DATE.
I ran the query below to exemplify how it should be:
WITH
a AS (
SELECT
"2020-01-31" AS date )
SELECT
FORMAT_DATE("%Y%m%d", CAST(date AS DATE)) AS new_date
FROM
a
I hope it helps.

How to make conditional order by statement with multiple data types, sql oracle?

I tried to do so, but my data types are char and date
select * from table_name
where
smthng
order by
case when to_date(to_char( :edate, 'dd.mm.yyyy'),'dd.mm.yyyy')-
to_date(to_char( :sdate, 'dd.mm.yyyy'),'dd.mm.yyyy')>0
then ddate
else department end
You don't need any conversion for substraction considering :edate and :sdate are of type date
select *
from table_name
where smthng
order by case
when :edate - :sdate > 0 then
to_char(ddate ,'dd.mm.yyyy')
else
department
end
All the outputs from a CASE statement must have the same data-type and since you can't convert department to a DATE data-type then you will need to convert ddate to a String or else have two CASE statements with mutually exclusive conditions.
Some additional points:
Firstly, you don't need to convert :sdate and :edate from a DATE data type to a string and then back to a DATE; just perform the comparison on the DATEs.
Secondly, as you are doing this in the ORDER BY clause then you need to make sure what you are ordering by makes sense. Ordering by a date formatted as a 'dd.mm.yyyy' string does not make sense as it will perform an alpha-numeric string comparison and not a comparison in ascending date order. If you do want to use a single case statement and convert both to strings then use an ISO 8601 format YYYY-MM-DD which will let you order the formatted date string using an alpha-numberic sort.
Like this:
SELECT *
FROM table_name
WHERE something
ORDER BY
CASE
WHEN :edate > :sdate
THEN TO_CHAR( ddate ,'YYYY-MM-DD' )
ELSE department
END
or you can just use 2 CASE statements, one for each mutually exclusive case and defaulting each to NULL (or some other constant) when it does not match:
SELECT *
FROM table_name
WHERE something
ORDER BY
CASE WHEN :edate > :sdate THEN ddate ELSE NULL END,
CASE WHEN :edate > :sdate THEN NULL ELSE department END;
The value in order must match the data type so if you need can't return date or string but a single data type only A8ssuming department is a strint data type you should convert data as char)
select *
from table_name
where ....... smthng ....
order by
case when to_date(to_char( :edate, 'dd.mm.yyyy'),'dd.mm.yyyy')-
to_date(to_char( :sdate, 'dd.mm.yyyy'),'dd.mm.yyyy')>0
then to_char(ddate , 'dd.mm.yyyy')
else department
end

Oracle SQL Case with Condition

I have a question regarding Oracle SQL case statement.
in the where condition I would like to apply the following condition.
if salary_date is null then effective_date should be greater than 01-Jan-2016
I have the tried as
case when salary_date is null then
trunc(effective_date) >= '01-JAN-2016' else
null end
However the above resulted in
ORA-00933: SQL command not properly ended
How can I resolve this?
The problem with your code is that SQL interpreter expects to see the value after 'then' keyword, whereas you have a condition clause.
Try something like this maybe:
case when salary_date is null and trunc(effective_date) >= '01-JAN-2016'
then <value you need>
else null
You can try this without CASE statement
SELECT
..
..
WHERE ( trunc(effective_date) >= '01-JAN-2016' AND salary_date is null )
OR ( <some other condition> )
...where salary_date is not null or effective_date >= date '2016-01-01'
Since Oracle SQL does not have "IF... THEN", use basic logic to transform your boolean expression. IF a THEN b is the same thing as (NON a) OR b. This is what I did above.
DO...NOT... compare dates to strings. '01-JAN-2016' is a string, not a date. You MUST convert it to a date, for example with to_date('01-JAN-2016', 'dd-MON-yyyy').
Or, as an alternative, note how I input a "date literal" (a fixed date). If I don't need to input a time of day, I can use the expression date '2016-01-01', which is a SQL Standard (ANSI) "date literal". Then you don't need to give a date format model; it MUST ALWAYS be in the exact format yyyy-mm-dd.

Using 'Between' operator after 'THEN' within 'CASE' statement within 'WHERE' Clause

Is it possible to use between operator within a CASE statement within a WHERE Clause ? For example in the code below, the condition should be pydate between (sysdate-12) and (sysdate-2) if its a monday and pydate between (sysdate-11) and (sysdate-1) if its a tuesday and so on. But the following doesn't work. May be there is another way of writing this. Can someone please help ?
select * from table_name
where pricekey = 'JUF' and
case when to_char(to_date(sysdate,'DD-MON-YY'), 'DY')='MON' then pydate between to_date(sysdate-12,'DD-MON-YY') and to_date(sysdate-2,'DD-MON-YY')
when to_char(to_date(sysdate,'DD-MON-YY'), 'DY')='TUE' then pydate between to_date(sysdate-11,'DD-MON-YY') and to_date(sysdate-1,'DD-MON-YY')
else pydate='sysdate'
end
You can apply the logic you are attempting, but it is done without the CASE. Instead, you need to create logical groupings of OR/AND to combine the BETWEEN with the other matching condition from your case.
This is because CASE is designed to return a value, rather than to dynamically construct the SQL inside it.
SELECT *
FROM table_name
WHERE
pricekey = 'JUF'
AND (
-- Condition 1
(to_char(to_date(sysdate,'DD-MON-YY'), 'DY') = 'MON' AND pydate BETWEEN to_date(sysdate-12,'DD-MON-YY') AND to_date(sysdate-2,'DD-MON-YY'))
-- Condition 2
OR (to_char(to_date(sysdate,'DD-MON-YY'), 'DY')='TUE' AND pydate BETWEEN to_date(sysdate-11,'DD-MON-YY') AND to_date(sysdate-1,'DD-MON-YY'))
-- ELSE case, matching neither of the previous 2
OR (to_char(to_date(sysdate,'DD-MON-YY'), 'DY') NOT IN ('MON', 'TUE') AND pydate = 'sysdate')
)
This is hard to write using a case. Just do:
where pricekey = 'JUF' and
((to_char(to_date(sysdate,'DD-MON-YY'), 'DY') = 'MON' and
pydate between to_date(sysdate-12,'DD-MON-YY') and to_date(sysdate-2,'DD-MON-YY')
) or
(to_char(to_date(sysdate,'DD-MON-YY'), 'DY') = 'TUE' and
pydate between to_date(sysdate-11,'DD-MON-YY') and to_date(sysdate-1,'DD-MON-YY')
) or
(o_char(to_date(sysdate,'DD-MON-YY'), 'DY') not in ('MON', 'TUE') and
pydate = trunc(sysdate)
)
)
Note, I also removed the single quotes around "sysdate", so it won't be treated as a string. And, I trunc'ed it to just get the date portion with no time.
First of all, sysdate is a date so don't use to_date on it.
Secondly, don't rely on day names. Once another language setting is chosen, your statement may not work anymore.
Then pydate is a date. sysdate is a date. Dont compare pydate to the string 'sysdate', but to sysdate.
It also seems that you are trying to remove the time part from sysdate with to_date(sysdate-11,'DD-MON-YY'). The function used for removing a time part of a datetime is trunc.
Then the case statement is supposed to return a value. There is no boolean data type in SQL, so you cannot evaluate an expression inside and return the result. Rather than doing so, you will return a valid data type such as a number or a string which you can compare outside:
select *
from table_name
where pricekey = 'JUF' and
case
when to_char(sysdate, 'DY', 'NLS_DATE_LANGUAGE=AMERICAN')='MON' then
case when pydate between trunc(sysdate)-12 and trunc(sysdate)-2 then 'GOOD' else 'BAD' end
when to_char(sysdate, 'DY', 'NLS_DATE_LANGUAGE=AMERICAN')='TUE' then
case when pydate between trunc(sysdate)-11 and trunc(sysdate)-1 then 'GOOD' else 'BAD' end
else
case when pydate = trunc(sysdate) then 'GOOD' else 'BAD' end
end = 'GOOD';
If pydate can have a time part, you may want to replace pydate with trunc(pydate), too.
Of course you can do all this with some ANDs and ORs and parentheses instead of CASE statements. It's up to you what is more readable for you.