oracle query help using NVL similar function to set default - sql

How do I rewrite this oracle query, in the case I have no rows returned and want to hardcode a default value of '0' for a count and the sysdate information?
My query now will give me this if there is no data:
1* SELECT count(*) as MYCNT, timestamp FROM TESTDATA WHERE timestamp = to_char(sysdate-2, 'yyyymmdd') || '0000' group by timestamp
SQL> /
no rows selected
Here, I tried NVL, but not getting expected output:
1* select nvl(count(*), 0) as MYCNT, to_char(sysdate-2, 'yyyymmdd') || '0000' from TESTDATA WHERE timestamp = to_char(sysdate-2, 'yyyymmdd') || '0000' group by timestamp
SQL> /
no rows selected
Want to see something like this:
MYCNT TIMESTMP
----- --------
0 201107250000

The group by is preventing this from just working by defult.
select count(*) , to_char(sysdate-2, 'yyyymmdd') || '0000' as timestamp
from dual where 1=0
You can see this is the case by comparing the output to:
select count(*) , to_char(sysdate-2, 'yyyymmdd') || '0000' as timestamp
from dual where 1=0
group by to_char(sysdate-2, 'yyyymmdd') || '0000'
Second version returns blank, first version returns 0 and the timestamp

try
SELECT 0, to_char(sysdate-2, 'yyyymmdd') || '0000' FROM DUAL WHERE 0 =
(SELECT count(*) FROM TESTDATA WHERE timestamp = to_char(sysdate-2, 'yyyymmdd') || '0000')
UNION
SELECT count(*) as MYCNT, timestamp FROM TESTDATA WHERE timestamp = to_char(sysdate-2, 'yyyymmdd') || '0000' group by timestamp
this return the real data if it is present and if not the default

Try this:
SELECT count(*) as MYCNT, timestamp
FROM TESTDATA
WHERE timestamp = to_char(sysdate-2, 'yyyymmdd') || '0000'
GROUP BY timestamp
UNION
SELECT 0, '201107250000'
FROM dual

Related

What would be the best way to filter part of the timestamp?

For example, I have a TIMESTAMP field and a record is something like '2021-04-23 14:17:46' in my database, and I want to find the records that start with '2021-04-23', that is a part of the TIMESTAMP, in SQL. What would be the best way to filter part of the timestamp?
And the other way around? If I have something like '2021-03-04' and I want to find all the timestamps that are yyyy-MM-dd HH: mm: ss
If your parameter is a valid date string, use date arithmetic. For example
-- parameter
with prm as (
select '2021-04-23' pd from dual
),
-- sample data
tbl(Code, Dt) as (
select 'c1', TIMESTAMP '2021-04-23 12:17:46' from dual union all
select 'c2', TIMESTAMP '2021-04-24 14:17:46' from dual
)
--
select tbl.*
from tbl
cross join prm
where dt >= TO_DATE(prm.pd, 'YYYY-mm-DD') and dt < TO_DATE(prm.pd, 'YYYY-mm-DD') + 1
order by code, dt
select dttm_col
from table
where to_date(dttm_col) = '2020-01-20'
The simple way is to TRUNCate the timestamp and compare it to a literal:
SELECT *
FROM table_name
WHERE TRUNC( timestamp_column ) = DATE '2021-04-23'
This may be simple but its not the best solution as Oracle will not use any index on the timestamp_column and will perform a full table scan; to use an index you would need to create a function-based-index on TRUNC( timestamp_column ).
The better way, although slightly more complicated, is to compare on a range of values using a TIMESTAMP literal:
SELECT *
FROM table_name
WHERE timestamp_column >= TIMESTAMP '2021-04-23 00:00:00'
AND timestamp_column < TIMESTAMP '2021-04-23 00:00:00' + INTERVAL '1' DAY;
or, using DATE literals:
SELECT *
FROM table_name
WHERE timestamp_column >= DATE '2021-04-23'
AND timestamp_column < DATE '2021-04-23' + INTERVAL '1' DAY;
If you want to compare to a string, rather than a literal, then use TO_TIMESTAMP:
SELECT *
FROM table_name
WHERE timestamp_column >= TO_TIMESTAMP( '2021-04-23', 'YYYY-MM-DD' )
AND timestamp_column < TO_TIMESTAMP( '2021-04-23', 'YYYY-MM-DD' ) + INTERVAL '1' DAY;
or, to remove the time component:
SELECT *
FROM table_name
WHERE timestamp_column >= TO_TIMESTAMP( SUBSTR( '2021-04-23 14:17:46', 1, 10 ), 'YYYY-MM-DD' )
AND timestamp_column < TO_TIMESTAMP( SUBSTR( '2021-04-23 14:17:46', 1, 10 ), 'YYYY-MM-DD' ) + INTERVAL '1' DAY;

ORA-01722: invalid number on subquery

Hi i'm trying to execute my query and unfortunately trows me wih error of ORA-01722: invalid number
SELECT L.NEVENTLOGIDN, LPAD (nuserid, 6, '0') nuserid, u.susername,
TO_CHAR (TO_DATE ('1970-01-01 00:00:00', 'YYYY-MM-DD HH24:MI:SS') + ( (ndatetime) / (60 * 60 * 24)), 'YYYY-MM-DD HH24:MI:SS')
date_time, l.nreaderidn, r.sname,
CASE WHEN l.nreaderidn IN (SELECT GETREADERSBYFUNC('OUT', 'LOCKER') devices FROM dual ) THEN 'O'
WHEN l.nreaderidn IN (SELECT GETREADERSBYFUNC('IN', 'LOCKER') devices FROM dual ) THEN 'I' END logtype
FROM TB_EVENT_LOG l, TB_READER r, TB_USER u
WHERE
l.nreaderidn IN ( SELECT GETREADERSBYDESC('LOCKER') devices FROM dual)
AND NDATETIME >= ((TO_DATE ('2020-01-27' || ' 12:00:00 AM', 'YYYY-MM-DD HH:MI:SS AM') ) - TO_DATE ('1970-01-01 12:00:00 AM', 'YYYY-MM-DD HH:MI:SS AM')) * 24 * 60 * 60
AND ndatetime <= ((TO_DATE ('2020-01-28' || ' 12:00:00 PM', 'YYYY-MM-DD HH:MI:SS PM') ) - TO_DATE ('1970-01-01 12:00:00 AM', 'YYYY-MM-DD HH:MI:SS AM')) * 24 * 60 * 60
AND l.nuserid = u.suserid
AND l.nreaderidn = r.nreaderidn
ORDER BY 2, 4
I think the reason of error is this below.
1
CASE WHEN l.nreaderidn IN (SELECT GETREADERSBYFUNC('OUT', 'LOCKER') devices FROM dual ) THEN 'O'
sample data of GetReaderbyfunc
'544381428','544381436','544381433','544381424','544381043'
2
WHEN l.nreaderidn IN (SELECT GETREADERSBYFUNC('IN', 'LOCKER') devices FROM dual ) THEN 'I' END logtype
3
WHERE
l.nreaderidn IN ( SELECT GETREADERSBYDESC('LOCKER') devices FROM dual)
SELECT GETREADERSBYFUNC('OUT', 'LOCKER') devices FROM dual
result is below
'544381428','544381436','544381433','544381424','544381043'
because the nreaderidn is a number type but when i put the result of it like bellow it is working
SELECT GETREADERSBYDESC('LOCKER') devices FROM dual
result is bellow
'544381050','544381441','544381428','544381436','544381431','544381064','544381433','544381435','544381424','544381043'
WHERE
l.nreaderidn IN ( '544381428','544381436','544381433','544381424','544381043')
functions
getreadersbyfunc (p_func VARCHAR2, p_desc VARCHAR2)
RETURN VARCHAR2
IS
retVal VARCHAR2(1024);
BEGIN
for cur_rec in (SELECT nreaderidn FROM tb_reader where sdescription = p_desc and upper(sname) like '%' || upper(p_func) || '%')
loop
if retVal is NULL then
retVal := '''' || cur_rec.nreaderidn || '''';
else
retVal := retVal || ',''' || cur_rec.nreaderidn || '''';
end if;
end loop;
return retVal;
EXCEPTION
WHEN NO_DATA_FOUND THEN
NULL;
WHEN OTHERS THEN
RAISE;
getreadersbydesc (p_description VARCHAR2)
RETURN VARCHAR2
IS
retVal VARCHAR2(1024);
BEGIN
for cur_rec in (SELECT nreaderidn FROM tb_reader where sdescription = p_description)
loop
if retVal is NULL then
retVal := '''' || cur_rec.nreaderidn || '''';
else
retVal := retVal || ',''' || cur_rec.nreaderidn || '''';
end if;
end loop;
return retVal;
EXCEPTION
WHEN NO_DATA_FOUND THEN
NULL;
WHEN OTHERS THEN
RAISE;
END;
I'm not allowed to change the data type of nreaderidn and to modify the oracle function.
Is There a way to solve this?
Thank you in advance
Your function returns a comma separated list of numbers, which needs to be split.
Try th
Eg:
SELECT
regexp_substr(GETREADERSBYFUNC('OUT', 'LOCKER'), '[^,]+', 1, level)
FROM dual
connect by
regexp_substr(GETREADERSBYFUNC('OUT', 'LOCKER'), '[^,]+', 1, level) is not null
Your query would then look like:
SELECT L.NEVENTLOGIDN, LPAD (nuserid, 6, '0') nuserid, u.susername,
TO_CHAR (TO_DATE ('1970-01-01 00:00:00', 'YYYY-MM-DD HH24:MI:SS') + ( (ndatetime) / (60 * 60 * 24)), 'YYYY-MM-DD HH24:MI:SS')
date_time, l.nreaderidn, r.sname,
CASE WHEN l.nreaderidn IN (SELECT
regexp_substr(GETREADERSBYFUNC('OUT', 'LOCKER'), '[^,]+', 1, level) FROM dual
connect by regexp_substr(GETREADERSBYFUNC('OUT', 'LOCKER'), '[^,]+', 1, level) is not null ) THEN 'O'
WHEN l.nreaderidn IN (SELECT
regexp_substr(GETREADERSBYFUNC('IN', 'LOCKER'), '[^,]+', 1, level) FROM dual
connect by regexp_substr(GETREADERSBYFUNC('IN', 'LOCKER'), '[^,]+', 1, level) is not null) THEN 'I' END logtype
FROM TB_EVENT_LOG l, TB_READER r, TB_USER u
WHERE
l.nreaderidn IN ( SELECT
regexp_substr(GETREADERSBYFUNC('LOCKER'), '[^,]+', 1, level) FROM dual
connect by regexp_substr(GETREADERSBYFUNC('LOCKER'), '[^,]+', 1, level) is not null)
AND NDATETIME >= ((TO_DATE ('2020-01-27' || ' 12:00:00 AM', 'YYYY-MM-DD HH:MI:SS AM') ) - TO_DATE ('1970-01-01 12:00:00 AM', 'YYYY-MM-DD HH:MI:SS AM')) * 24 * 60 * 60
AND ndatetime <= ((TO_DATE ('2020-01-28' || ' 12:00:00 PM', 'YYYY-MM-DD HH:MI:SS PM') ) - TO_DATE ('1970-01-01 12:00:00 AM', 'YYYY-MM-DD HH:MI:SS AM')) * 24 * 60 * 60
AND l.nuserid = u.suserid
AND l.nreaderidn = r.nreaderidn
ORDER BY 2, 4
This will not give you an error:
SELECT *
from TB_READER l
where cast(l.nreaderidn as varchar2(1024)) in (select GETREADERSBYDESC('LOCKER') devices FROM dual)
Just use the cast of the nreaderidn column.
But also be aware that you will search a varchar like for example '2' in the string(not an series of characters but one string) '1','2','3' so this will not retun any results.
Here is also a small example where you can see the errir being produced without a cast and solved when the cast is added:
https://dbfiddle.uk/?rdbms=oracle_11.2&fiddle=776ecd951aff56500938ac064b34788c

Convert Decimal to Date in DB2

I have a column in my table having date value as Decimal like 20180715 for the date 15-07-2018.
I want to convert it to MMDDYYYY format.
Example:
Given decimal value 20180715 is converted to 07152018.
How to do it ?
try somthing like this:
select
VARCHAR_FORMAT( TIMESTAMP_FORMAT(cast(yourcolumn as varchar(8)), 'YYYYMMDD') , 'MMDDYYYY')
from yourtable
but you you want a really date do it:
select
DATE( TIMESTAMP_FORMAT(cast(yourcolumn as varchar(8)), 'YYYYMMDD'))
from yourtable
Try this query for the date
select
date(timestamp_format(char(yourcolumn+19000000), 'YYYYMMDD'))
from yourtable
To get the time
select
cast( substr( right( '00' || yourcolumn, 6) ,1,2) || ':' || substr( right( '00' || yourcolumn, 6) ,3,2) || ':' || substr( right( '00' || yourcolumn, 6) ,5,2) as time)
from yourtable

Get data in range using oracle

Please help me to solve this.
I have a table that contain users check in (checktype = I) and check out (checktype = 0) time everyday, and I would like to get the total amount of check in time per user which occur > 08:00 AM in a specific date range.
I am using the query below, but only handle one day per query not in a range, so I have to loop using javascript to get the amount of delay ( > 08:00 AM) per user for example from 01/06/2012 to 06/06/2012
Please help me to get the amount (count) check in time > 08:00 AM per user (ex: userid 708) from ex:01/06/2012 to 06/06/2012 in a single query.
with tt as
(
select TO_DATE('01/06/2012 08:00:00','dd/mm/yyyy hh24:mi:ss') date1 ,
checktime date2
from
checkinout
where
userid = '708' and
to_char(checktime,'dd/mm/yyyy') = '01/06/2012' and
checktype='I' -- checktype I is check in
) , t2 as
(
select numtodsinterval(date2 - date1,'day') dsinterval from tt
)
select extract(hour from dsinterval) || ' hours ' ||
extract(minute from dsinterval) || ' minutes ' ||
round(extract(second from dsinterval)) || ' seconds' late from t2
I assume you wanted to get how many hours late (i.e. after 08:00) the checkins have been done:
with t2 as (
select userid
,numtodsinterval(sum(checktime - (trunc(checktime)+8/24)),'day') dsinterval
,count(1) cnt
from checkinout
where userid='708'
and checktime > trunc(checktime)+8/24
and trunc(checktime) between to_date('01/06/2012','DD/MM/YYYY') and to_date('06/06/2012','DD/MM/YYYY')
and checktype = 'I'
group by userid
)
select extract(hour from dsinterval) || ' hours ' ||
extract(minute from dsinterval) || ' minutes ' ||
round(extract(second from dsinterval)) || ' seconds' late
,cnt
from t2;
See http://sqlfiddle.com/#!4/c4670/11 for my test case.
edit: added column "cnt" to show how many times
Consider the following example on base of this you can write your own logic
WITH tbl AS
(SELECT SYSDATE dt
FROM DUAL
UNION
SELECT SYSDATE + (1 + (10 / 1440))
FROM DUAL
UNION
SELECT SYSDATE + (2 + (12 / 1440))
FROM DUAL
UNION
SELECT SYSDATE + (3 + (13 / 1440))
FROM DUAL
UNION
SELECT SYSDATE + (6 + (15 / 1440))
FROM DUAL
UNION
SELECT SYSDATE + (8 + (18 / 1440))
FROM DUAL)
SELECT EXTRACT (HOUR FROM dsinterval)
|| ' hours '
|| EXTRACT (MINUTE FROM dsinterval)
|| ' minutes '
|| ROUND (EXTRACT (SECOND FROM dsinterval))
|| ' seconds' late
FROM (SELECT NUMTODSINTERVAL (dt1 - dt2, 'day') dsinterval
FROM (SELECT TO_DATE (TO_CHAR (dt, 'DD/MM/YYYY') || ' 08:00:00',
'DD/MM/YYYY HH24:MI:SS'
) dt1,
TO_DATE (TO_CHAR (dt, 'DD/MM/YYYY HH24:MI:SS'),
'DD/MM/YYYY HH24:MI:SS'
) dt2
FROM tbl
WHERE dt BETWEEN SYSDATE + 2 AND SYSDATE + 5))
As per code you can write like
SELECT EXTRACT (HOUR FROM dsinterval)
|| ' hours '
|| EXTRACT (MINUTE FROM dsinterval)
|| ' minutes '
|| ROUND (EXTRACT (SECOND FROM dsinterval))
|| ' seconds' late
FROM (SELECT NUMTODSINTERVAL (dt1 - dt2, 'day') dsinterval
FROM (SELECT TO_DATE (TO_CHAR (checktime , 'DD/MM/YYYY') || ' 08:00:00',
'DD/MM/YYYY HH24:MI:SS'
) dt1,
TO_DATE (checktime, 'DD/MM/YYYY HH24:MI:SS') dt2
FROM checkinout
WHERE checktime BETWEEN start_date AND end_date
AND checktype='I'))

In Oracle, how do I convert a number such as 1 to a string such as "1st"?

I'd like to format a number as "1st", "2nd", "4th", "9th", etc. Is there an Oracle function that will do this for me?
Assuming the value supplied is numeric, rather than DATE, you can use TO_CHAR but you have to convert the numeric value to a string, then a DATE (Julian) before ultimately formatting it:
SELECT TO_CHAR(TO_DATE('1', 'dd'), 'ddth')
FROM DUAL
Result:
01st
When testing, using 'd' for the format didn't return expected results because the value is interpreted as a Julian date. Either substring the output to remove the leading zero, or provide a full date string (doesn't matter to the TO_CHAR because it's only interested in the day of the month):
SELECT TO_CHAR(TO_DATE('1900-01-01', 'YYYY-MM-dd'), 'dth')
FROM DUAL
Because calendar days end at 31, use the year value instead to handle numbers greater than 31:
SELECT TO_CHAR(TO_DATE('32-01-01', 'YYYY-MM-dd'), 'yyth')
FROM DUAL
Result:
32nd
Maybe I'm oversimplifying, but it seems like the following should work just fine (for integers) and is a lot more readable than converting to a date and back:
select case
when initial_extent is null then null
when substr(initial_extent,-2,1) = '1'
then initial_extent || 'th'
else case substr(initial_extent,-1,1)
when '1' then initial_extent || 'st'
when '2' then initial_extent || 'nd'
when '3' then initial_extent || 'rd'
else initial_extent || 'th'
end
end as formatted_number
from user_tables
select substr( to_char( to_date( abs( decode( mod( l_value, 10 ), 0, 4, mod( l_value , 10 ) ) ), 'YYYY' ), 'YTH' ), 2 ) as value
from dual
Replace l_value with appropriate, hmmm, value. Should cover any numbers.