Using case when to insert values - sql

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.

Related

Oracle query, get count of records by hour

I am trying to get transaction counts for every hour. Normally it is a straight forward query by unfortunately the timestamp column I have to work with is not timestamp but varchar2! No matter what I try I get either "not a valid month" or "invalid number", depending on the format I use.
The timestamp looks like: 2021-08-08 00:00:52:632
I also executed the following to get NLS format:
SELECT * FROM nls_session_parameters WHERE parameter = 'NLS_DATE_FORMAT';
and get
DD-MON-RRRR.
This is the latest I tried among a dozen others (I commented out the "group by" to just get the darn thing to display).
select to_char(reqts,'mm/dd/yyyy hh24') DATE_HR
--, count(*)
from idcreqresplog
where logdate > trunc(SYSDATE -2)
and logtypeid in (2,4)
--group by to_char(reqts,'mm/dd/yyyy hh24');
Also
select to_char(reqts, 'yyyy-mm-dd hh24:mi:ss.fff' )
--, count(*)
FROM
reqresplog
WHERE
logdate > trunc(SYSDATE -2) ;
--group by to_date(reqts, 'yyyy-mm-dd HH4');
At my wits end and need some help.
Assuming that your column is always in the format 2021-08-08 00:00:52:63 then group on the substring up to the 13th character:
SELECT SUBSTR(reqts, 1, 13) AS date_hr,
count(*)
FROM idcreqresplog
WHERE logdate > trunc(SYSDATE -2)
AND logtypeid in (2,4)
GROUP BY
SUBSTR(reqts, 1, 13);
If you do want to convert to a date then, from Oracle 12.2, you can use TO_TIMESTAMP(string_value DEFAULT NULL ON CONVERSION ERROR, 'YYYY-MM-DD HH24:MI:SS:FF'):
SELECT TRUNC(
TO_TIMESTAMP(
reqts DEFAULT NULL ON CONVERSION ERROR,
'YYYY-MM-DD HH24:MI:SS:FF'
),
'HH'
) AS date_hr,
COUNT(*)
FROM idcreqresplog
WHERE logdate > trunc(SYSDATE -2)
AND logtypeid in (2,4)
GROUP BY
TRUNC(
TO_TIMESTAMP(
reqts DEFAULT NULL ON CONVERSION ERROR,
'YYYY-MM-DD HH24:MI:SS:FF'
),
'HH'
)
db<>fiddle here
Assuming as LittleFoot suggested, that some of your data is bad, you can use an inline WITH function to root out your bad data. Take the following example:
WITH FUNCTION get_timestamp
(
p_sTimeString VARCHAR2
)
RETURN TIMESTAMP
IS
BEGIN
RETURN TO_TIMESTAMP(p_sTimeString, 'YYYY-MM-DD HH24:MI:SS.FF3');
EXCEPTION WHEN OTHERS THEN RETURN NULL;
END;
SELECT TO_CHAR(s.hour, 'YYYY-MM-DD HH24') AS HOUR, COUNT(*) AS ROW_COUNT
FROM (SELECT TRUNC(get_timestamp(td.time), 'HH24') AS HOUR,
td.amount
FROM test_data td) s
WHERE s.hour IS NOT NULL
GROUP BY s.hour
ORDER BY s.hour;
Here is the DBFiddle showing this working for some good and bad data (Link).
What the query does is use an inline function to call the TO_TIMESTAMP function. Then it just catches any error and returns NULL. This saves you from your bad data messing up your query. After that, the query is pretty much as you had tried earlier. I truncate the timestamp to the hour in the inner query and then use that to group by in the outer query (Only using the rows which don't have NULL timestamps...meaning they didn't error)

PostgreSQL how to get closest birthday date

I have a table called birthdays with 2 columns name and date.
Name is string value, date is date (Looks like this: 1989-07-28 00:00:00)
How can i get closest birthday, according to day i am checking, for example NOW()
Using PostgreSQL
The question is determining if the year is this year or next year:
select (case when to_char(dob, 'MM-DD') >= to_char(now(), 'MM-DD')
then to_date(to_char(current_date, 'YYYY') || '-' || to_char(dob, 'MM-DD'), 'YYYY-MM-DD')
else to_date(to_char(current_date, 'YYYY') || '-' || to_char(dob, 'MM-DD'), 'YYYY-MM-DD') + interval '1 year'
end)
from (values ('1989-07-28'::date)) v(dob);
Assuming you are searching for forward-looking birthdays, the query below takes the minimum difference in days starting from current date
Select "Date" - Current_Date as diff,
"Date" as Dob,
Name
from birthdays
Where ("Date"- Current_Date) > 0
Order by 1 asc
limit 1;
First off in Postgres if your column is defined as DATE then it does not appear as "1989-07-28 00:00:00". Dates in Postgres do not have time components, so no "00:00:00" (unless you have done something with datestyle. But that is actually immaterial here.
Postgres dates can be directly subtracted to get the days number of between them, with the result either positive or negative depending upon which date occurs first. To
get the "closest" to a specific data just take the absolute value.
with birthdays (name, bday) as
( values ('George', date '2020-10-18')
, ('Gloryann', date '2020-11-02')
, ('Phyllis', date '2020-10-09')
, ('Sam', date '2020-06-18')
)
select name, bday birthday
from birthdays
order by abs(current_date-bday)
limit 1;
Caution: Do not use date as an object name. Date is both a Postres and SQL standard reserved word. While using it may be permitted currently Postges would be within their right to enforce the predefined meaning at anytime, potentially causing major issues for your app. Play it safe Do Not use reserved words as object names.
Thanks guys, it works perfect this way:
select name, date, (case when to_char(date, 'MM-DD') >= to_char(now(), 'MM-DD') then
to_date(to_char(current_date, 'YYYY') || '-' || to_char(date, 'MM-DD'), 'YYYY-MM-DD')
else to_date(to_char(current_date, 'YYYY') || '-' || to_char(date, 'MM-DD'), 'YYYY-MM-DD') + interval '1 year' end)
from birthdays order by 3

PostgreSQL CASE WHEN with operator returns null row

I'm trying to generate the pay period value based on the current date. When I ran the below query, it returns correct pay period, but I got additional null row. How can I get rid of null row?
select distinct case when current_date >= to_date(begin_payperiod_date, 'mm/dd/yy')
and current_date < to_date(end_payperiod_date, 'mm/dd/yy')
then cast(regexp_replace(itc_pp, '[^0-9]*', '', 'g') as integer)-1 end AS current_pp
from actacc.payperiod_conversion_all_years
Currently it returns:
1 null
2 18
I want to return only the 2nd row.
Move the case logic to a where clause:
select (cast(regexp_replace(itc_pp, '[^0-9]*', '', 'g') as integer)-1) AS current_pp
from actacc.payperiod_conversion_all_years
where current_date >= to_date(begin_payperiod_date, 'mm/dd/yy') and
current_date < to_date(end_payperiod_date, 'mm/dd/yy') ;
I removed the select distinct. You don't have samples of your data, so it is unclear if it is necessary. If it is you can add it back in.
Note that you should be storing dates using a proper date/time data types, not as strings. If you did that, the conversion step would be unnecessary.
You should use a where condition to filter rows:
select distinct cast(regexp_replace(itc_pp, '[^0-9]+', '', 'g') as integer)-1 AS current_pp
from actacc.payperiod_conversion_all_years
where current_date >= to_date(begin_payperiod_date, 'mm/dd/yy')
and current_date < to_date(end_payperiod_date, 'mm/dd/yy')
Also, it doesn't make sense to match empty strings just to replace them by empty strings again, so I've changed * to + in your regex.
In your query, You only defined the CASE for records belongs to-
current_date >= to_date(begin_payperiod_date, 'mm/dd/yy')
AND current_date < to_date(end_payperiod_date, 'mm/dd/yy')
As a result, all records out of that date range is returning NULL as you do not defined anything what if the date is out of that range. You can define a ELSE part for the record outside of that date range. The script can be as below-
SELECT
DISTINCT
CASE
WHEN current_date >= to_date(begin_payperiod_date, 'mm/dd/yy')
AND current_date < to_date(end_payperiod_date, 'mm/dd/yy')
THEN CAST(regexp_replace(itc_pp, '[^0-9]*', '', 'g') AS INTEGER) - 1
ELSE itc_pp
-- Here you can keep the original value or do some
-- adjustment as per requirement to keep sync in values data type.
END AS current_pp
FROM actacc.payperiod_conversion_all_years;
If you add the date filter condition in where clause, the script should be as below-
SELECT
DISTINCT CAST(regexp_replace(itc_pp, '[^0-9]*', '', 'g') AS INTEGER) - 1 current_pp
FROM actacc.payperiod_conversion_all_years
WHERE current_date >= to_date(begin_payperiod_date, 'mm/dd/yy')
AND current_date < to_date(end_payperiod_date, 'mm/dd/yy');

How to compare two dates in oracle11g

i am new to oracle. i need to compare date column with the current date in oracle 11g
for an example my table is
srno dob
1 1992-04-01
2 1988-04-01
3 1995-04-01
so i have to compre dob with the sysdate. if it is matched then must show the data.
i have tried this query to get result.
select dob
from xyz
where extract(month from dob)=extract(month from sysdate)
and extract(day from dob)=extract(day from sysdate);
but it is not working. please tell me where am i going wrong.
thanks.
select ...
where to_char(dob,'MMDD')=to_char(sysdate,'MMDD')
There are easier ways to compare two dates in Oracle. Try the solution below:
select random_date_1, random_date_2,
-- when you have to match the complete date
/* use to_char(random_date_1,'Dd-Mon-Yy hh24.mi.ss')
when comparing date time */
/* use to_char(random_date_1,'Dd-Mon-Yy hh24')
when only checking the date and hour (this is actually useful in a scenarios */
case when trunc(random_date_1) = trunc(random_date_2)
then 'Match' else 'No Match' end as method_1,
case when to_char(random_date_1,'Dd-Mon-Yy') = to_char(random_date_2,'Dd-Mon-Yy')
then 'Match' else 'No Match' end as method_2,
-- when you have to match only month
case when trunc(random_date_1,'Mon') = trunc(random_date_2,'Mon')
then 'Match' else 'No Match' end as method_3,
case when to_char(random_date_1,'Mon') = to_char(random_date_2,'Mon')
then 'Match' else 'No Match' end as method_4
from
(select to_date(round (dbms_random.value (24, 31))
|| '-'
|| round (dbms_random.value (01, 01))
|| '-'
|| round (dbms_random.value (2015, 2015)),
'DD-MM-YYYY') + level - 1 random_date_1,
to_date(round (dbms_random.value (27, 31))
|| '-'
|| round (dbms_random.value (01, 01))
|| '-'
|| round (dbms_random.value (2015, 2015)),
'DD-MM-YYYY') + level - 1 random_date_2 from dual
connect by level <= 10);
Try this
SELECT DOB
FROM XYZ
WHERE TRUNC (DOB) = TRUNC (SYSDATE)
As said above use to_char if you need to do a conversion, and look at the different format masks available. If your dob datatype is a date then you can compare directly with the SYSDATE value. As above getting just MMDD will give you just the month (04) and the day (01) as a char string for comparison.
I've just tried out the example you gave in your question and can't see what is wrong with using extract, as you have shown:
select dob
from xyz
where extract(month from dob)=extract(month from sysdate)
and extract(day from dob)=extract(day from sysdate);
Or am I misunderstanding something? You say that the code isn't working but I can't see what you mean.
I prefer to use extract rather than to_char in this sort of situation as I feel that it more clearly represents what I want. I don't want a character representation of the date, I just want to compare the month and the day.
Here is a SQLFiddle with an example: http://sqlfiddle.com/#!4/c545c/2
What is the error you are getting?
In your DB is dob field defined as DATE or varchar2?
In case your DB field is varchar2, then you may have to use,
SELECT * FROM XYZ
WHERE TRUNC ( TO_DATE(DOB, 'YYYY-MM-DD') ) = TRUNC (SYSDATE);

Oracle. Missing keyword when using case statement. Error 00905

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