Oracle. Missing keyword when using case statement. Error 00905 - sql

I keep getting an error 'ORA-00905: missing keyword' with the following statement, ever since I introduced the CASE statement, but I can't figure out what is missing.
SELECT
CYCLE_S_FACT_MAIN.STARTTIME,
CYCLE_S_FACT_MAIN.ENDTIME
FROM
CYCLE_S_FACT_MAIN
WHERE
(
CYCLE_S_FACT_MAIN.ENDTIME >
(SELECT SYSDATE,
CASE SYSDATE
WHEN TO_CHAR(SYSDATE, 'HH') < 6 THEN CONCAT(TO_CHAR(SYSDATE, 'DD-MM-YYYY'), ' 06:00:00')
ELSE CONCAT(TO_CHAR(SYSDATE - INTERVAL '1' DAY, 'DD-MM-YYYY'), ' 06:00:00')
END AS SYSDATE
FROM DUAL
)
AND
CYCLE_S_FACT_MAIN.ENDTIME <= SYSDATE
)

You're mixing up the two forms of CASE expressions. There's a simple expression (when you're just wanting to compare expressions for equality):
CASE Expr1
WHEN Expr2 THEN ...
WHEN Expr3 THEN ...
ELSE ...
END
And there's a searched CASE expression, where you want to evaluate separate predicates:
CASE
WHEN Predicate1 THEN ...
WHEN Predicate2 THEN ...
ELSE ...
END
For a searched CASE, you don't specify an expression between CASE and the first WHEN.

Damien_The_Unbeliever is right about mixing case styles, but you also don't need the subquery at all, and the one you have is getting two columns back - which you can't compare with a single value. You can just do this:
WHERE
CYCLE_S_FACT_MAIN.ENDTIME > CASE
WHEN TO_NUMBER(TO_CHAR(SYSDATE, 'HH24')) < 6
THEN TRUNC(SYSDATE) + INTERVAL '6' HOUR
ELSE TRUNC(SYSDATE) - INTERVAL '1' DAY + INTERVAL '6' HOUR END
AND CYCLE_S_FACT_MAIN.ENDTIME <= SYSDATE
This leaves the comparison as between two dates, rather than relying on implcit conversions. I've also used HH24; using HH would treat times between midday and 6pm the same as those between midnight and 6am, which I'm prety sure you didn't intend.

Try as
SELECT
CYCLE_S_FACT_MAIN.STARTTIME,
CYCLE_S_FACT_MAIN.ENDTIME
FROM
CYCLE_S_FACT_MAIN
WHERE
(
CYCLE_S_FACT_MAIN.ENDTIME >
(SELECT SYSDATE,
CASE
WHEN TO_CHAR(SYSDATE, 'HH') < 6 THEN CONCAT(TO_CHAR(SYSDATE, 'DD-MM-YYYY'), ' 06:00:00')
ELSE CONCAT(TO_CHAR(SYSDATE - INTERVAL '1' DAY, 'DD-MM-YYYY'), ' 06:00:00')
END AS "MY_SYSDATE"
FROM DUAL
)
AND
CYCLE_S_FACT_MAIN.ENDTIME <= SYSDATE
)
There two possible mistakes, one is it is expecting as CASE WHEN not as CASE sysdate WHEN and second one is sysdate as alias which is not possible to use as alias name.

SELECT TO_CHAR(SYSDATE,'HH'),
CASE
WHEN TO_CHAR(SYSDATE,'HH') < 6
THEN CONCAT(TO_CHAR(SYSDATE, 'DD-MM-YYYY'), ' 06:00:00')
ELSE CONCAT(TO_CHAR(SYSDATE - INTERVAL '1' DAY, 'DD-MM-YYYY'), ' 06:00:00')
END "sysdate" ,
TO_CHAR(SYSDATE, 'HH'),
CONCAT(TO_CHAR(SYSDATE, 'DD-MM-YYYY'), ' 06:00:00')
FROM dual;
12 24-07-2013 06:00:00 12 25-07-2013 06:00:00

Related

Obtain data from Friday, Saturday and Sunday if today is Monday SQL Oracle

I am looking to obtain all data in a table from yesterday in SQL Oracle.
This is simply enough using the WHERE clause, i.e,
SELECT *
FROM My_Data
WHERE TO_DATE(My_Data.Date,'YYYY-MM-DD') = TRUNC(SYSDATE)-1
However if I now need to add more logic where if the day of the query is a Monday (SYSDATE) then obtain data between Friday and Sunday.
Using a between statement is no issue, I'm just not sure if I can include in a where statement given I'm unable to use case statement here.
Thanks
SELECT
*
FROM
My_Data
WHERE
TO_DATE(My_Data.Date,'YYYY-MM-DD')
Between Case When To_Char(SYSDATE, 'DY') = 'MON' Then TRUNC(SYSDATE)-3 ELSE TRUNC(SYSDATE)-1 END
And TRUNC(SYSDATE)-1
You can use the Case expression in Where clause. Regards...
Don't use TO_DATE on a column that is already a date (and if it is a string then don't store dates as strings).
So you are not dependent on the date language session parameter, you can compare the date to the start of the ISO week (which is independent of language) and you can compare on a date range so that Oracle can use an index on your date column:
SELECT *
FROM My_Data
WHERE "DATE" < TRUNC(SYSDATE)
AND "DATE" >= CASE TRUNC(SYSDATE) - TRUNC(SYSDATE, 'IW')
WHEN 0 -- Monday
THEN TRUNC(SYSDATE) - 3
ELSE TRUNC(SYSDATE) - 1
END;
or:
SELECT *
FROM My_Data
WHERE "DATE" < TRUNC(SYSDATE)
AND ( ( TRUNC(SYSDATE) - TRUNC(SYSDATE, 'IW') = 0 AND "DATE" >= TRUNC(SYSDATE) - 3 )
OR "DATE" >= TRUNC(SYSDATE) - 1
);

How to query between hours and minute in Oracle?

I have the following query:
...
CASE WHEN TO_NUMBER(TO_CHAR(SYSDATE,'hh24')) BETWEEN 2 AND 4 THEN 0 else count(*) end....
But I'd like to change until 4:30AM only (04:31 not apply anymore). How can I do it? I now I can get with to_char(...,'hh24:mi') but it will be a char value.
Also, I have tried:
select TO_DATE(TO_CHAR(SYSDATE,'hh24:mi'),'HH24:MI') from dual;
but the date goes wrong.
I want something like it:
CASE WHEN TO_DATE(SYSDATE,'hh24:mi') BETWEEN TO_DATE('02:00','HH24:MI') AND TO_DATE('04:30','HH24:MI')
Actually you can still do a range comparison using text hh:mm strings:
CASE WHEN TO_CHAR(SYSDATE, 'hh24') >= '02:00' AND
TO_CHAR(SYSDATE, 'hh24') <= '04:30'
THEN 0 ELSE COUNT(*) END
One option is to use INTERVAL DAY TO SECOND data types:
CASE
WHEN (SYSDATE - TRUNC(SYSDATE)) DAY TO SECOND
BETWEEN INTERVAL '02:00:00' HOUR TO SECOND
AND INTERVAL '04:30:00' HOUR TO SECOND
THEN 0
ELSE count(*)
END
Or, you can do the same with arithmetic:
CASE
WHEN (SYSDATE - TRUNC(SYSDATE)) BETWEEN 2/24 AND 4.5/24
THEN 0
ELSE count(*)
END

How to put Case in Where Statement for Oracle SQL

For the query below, I'm trying to pull a specific date range depending on the current day of the month. If it's the 20th or less (e.g. "2/7/2020") then I want the date range for January. Otherwise, I want the date range for February. Is it possible to be done with a case statement? Or there is a better way?
SELECT
account,
start_date,
amount
FROM
table1
WHERE
CASE
WHEN (
SELECT
CAST(EXTRACT(DAY FROM sysdate) AS NUMBER)
FROM
dual
) <= 20 THEN
start_date
BETWEEN '2020-01-01' AND '2020-01-31'
ELSE start_date BETWEEN '2020-02-01' AND '2020-02-29'
END
You can do this by avoiding the case statement and using truncate the date - 20 to the month, e.g.:
SELECT account,
start_date,
amount
FROM table1
WHERE start_date >= TRUNC(SYSDATE - 20, 'mm')
AND start_date < add_months(TRUNC(dt - 20, 'mm'), 1);
If you really had to use a CASE expression (you can't use a CASE statement in SQL), you would need to do something like:
SELECT account,
start_date,
amount
FROM table1
WHERE start_date >= CASE WHEN to_char(SYSDATE, 'dd') <= '20' THEN add_months(TRUNC(SYSDATE, 'mm'), -1) ELSE TRUNC(SYSDATE, 'mm') END
AND start_date < CASE WHEN to_char(SYSDATE, 'dd') <= '20' THEN TRUNC(SYSDATE, 'mm') ELSE add_months(TRUNC(SYSDATE, 'mm'), 1) END;
N.B. if you're using a function, you don't need to wrap it in a select .. from dual, you can use it directly in the SQL statement.
I've also assumed that you want a dynamic range, e.g. if the day of the month is 20 or less, the range is for the previous month, otherwise the current month.
ETA: You would use the above two queries if there is an index on the start_date column, otherwise you could simply do:
SELECT account,
start_date,
amount
FROM table1
WHERE TRUNC(start_date, 'mm') = TRUNC(SYSDATE - 20, 'mm');
Case statements return single values. As such you should pull out the start date and you'll need two case statements.
select account, start_date, amount
from table1 where
start_date between
(case
when (select cast(extract(day from sysdate) as number) from dual) <= 20 then '2020-01-01'
else '2020-02-01'
end) and
(case
when (select cast(extract(day from sysdate) as number) from dual) <= 20 then '2020-01-31'
else '2020-02-29'
end)
One method subtracts 20 days and then gets the month boundary:
where start_date >= trunc(sysdate - interval '20' day, 'MON') and
start_date < trunc(sysdate - interval '20' day, 'MON') + interval '1' month
This approach is index (and partition) friendly -- an appropriate index on start_date can be used. It is also safe if start_date has time components.
Note: You can use sysdate without having to use a subquery.
You can use or operator with last_day function as following:
Select * from your_table
Where (
start_date <= trunc(sysdate,'mm') + 20
and start_date between trunc(sysdate,'mm') - interval '1' month and trunc(sysdate,'mm') - 1
)
Or
(
start_date > trunc(sysdate,'mm') + 20
and start_date between trunc(sysdate, 'mm') and last_day(sysdate)
)
This approach will use index on start_date, if any.
Cheers!!
select account, amount, start_date
from table1
where ( ( (select cast (extract (day from sysdate) as number) from dual) <= 20
and start_date between date '2020-01-01' and date '2020-01-31')
or ( (select cast (extract (day from sysdate) as number) from dual) > 20
and start_date between date '2020-02-01' and date '2020-02-29')
);
Using CASE expressions as BETWEEN operands:
SELECT account
, start_date
, amount
FROM table1
WHERE start_date BETWEEN CASE
WHEN extract(day from sysdate) <= 20
THEN trunc(sysdate -interval '1' month, 'month')
ELSE trunc(sysdate, 'month')
END
AND CASE
WHEN extract(day from sysdate) <= 20
THEN last_day(sysdate -interval '1' month)
ELSE last_day(sysdate)
END

Error with selecting sysdate between time range

I want to fetch the data for sysdate-1 '00:00:00' between '23:59:59' time range. I have tried, but i am getting error.
This is my query..
select id_value, tn, LINE_TYPE_ID, ACCOUNT_TYPE_ID, PORT_ID, CURRENT_STATUS_ID, ERROR_CODE, ERROR_DESC, CRM_APP_CODE
from np_tn_dtls a
join np_port_req_dtls b on A.NP_TXN_ID=B.NP_TXN_ID
left join np_subscriber_dtls c on a.NP_TXN_ID=C.NP_TXN_ID
left join np_subscriber_id_dtls d on D.SUBSCRIBER_ID=C.SUBSCRIBER_ID
where (b.REQUEST_START_TIME between sysdate-1 '00:00:00'
AND sysdate '23:59:59') and (b.port_type_id != 2);
Please suggest me.
This expression is wrong:
between sysdate-1 '00:00:00'
AND sysdate '23:59:59'
Use trunc to round the date, so you can write this:
between trunc(sysdate-1) AND trunc(sysdate)
However, between is up to and including, so to prevent getting also request that happened exactly at midnight today, you can better write it like this:
b.REQUEST_START_TIME >= trunc(sysdate-1) AND
b.REQUEST_START_TIME < trunc(sysdate)
sysdate '23:59:59' is not a valid construction
use
b.REQUEST_START_TIME >= trunc(sysdate-1)
AND b.REQUEST_START_TIME < trunc(sysdate)

Using case when to insert values

Below shows a executable statement:
Successful attempt:
INSERT INTO Personnel_Assignment (DATE, testno, Hours)
SELECT '21-OCT-2011', '12345',
CASE
WHEN Extract(day From(S.ENDTIME-S.STARTTIME) ) >= 1
THEN (Extract(Day From(S.ENDTIME-S.STARTTIME) ) * 24
+ Extract(Hour From(S.ENDTIME-S.STARTTIME) ) )
WHEN S.endtime IS NULL
THEN NULL
ELSE
Extract(Hour From(S.ENDTIME-S.STARTTIME) ) )
||'hrs' End ||
Extract(Minute From(S.ENDTIME-S.STARTTIME) ) || 'Min' As Hours
FROM Schedule S`
Please note that the data type for endtime and start time is timestamp with timezone in this format:
Nls_Timestamp_Tz_Format='HH24:MI TZR'
Just a question that i would like to ask:
My datatype for hours is varchar2
And if i wish to sum my hours in the end from the results above, would it be tedious in converting it into number?
Thanks
First of all, || Else doesn't make sense. The part after || has to be another expression to concatenate.
Secondly, you certainly can nest case expressions, but in your case you don't need to. A single case expression can have multiple when/then branches, in the form case when [condition_A] then [expression_if_A_is_true] when [condition_B] then [expression_if_A_is_false_and_B_is_true] else [expression_if_A_and_B_are_both_false] end.