Conversion of number to date affect the performance in oracle - oracle

I have below query where the DT_ID is number data type column and the value stored in this column is date in the format 'YYYYMMDD'.
In my below query I want to get the previous week data from Monday till Sunday for weekly reporting and this query works very fast.
select ID,NAME
from TEST_REPORT
where
DT_ID between 20170904 and 20170910;
But this is hardcoded date and I want to make it dynamic. I tried with this query:
select ID,NAME
from TEST_REPORT
where
DT_ID>= next_day(trunc(sysdate), 'MONDAY') - 14 and
DT_ID< next_day(trunc(sysdate), 'MONDAY') - 7;
But it gives this error:
expecting Number and got date
When I convert this number to date like below query it's resulting a lot of performance issue. Is there any other way Ican make it dynamic with better performance?
select ID,NAME
from TEST_REPORT
where
to_date(DT_ID,'YYYYMMDD') >= next_day(trunc(sysdate), 'MONDAY') - 14 and
(DT_ID,'YYYYMMDD') < next_day(trunc(sysdate), 'MONDAY') - 7;

You can convert the date values to strings and then to numbers:
select ID,NAME
from TEST_REPORT
where DT_ID >= to_number (to_char (next_day(trunc(sysdate), 'MONDAY') - 14,
'YYYYMMDD'))
and DT_ID < to_number (to_char (next_day(trunc(sysdate), 'MONDAY') - 7,
'YYYYMMDD'));

Related

oracle - how to order by with date in the format dd-MMM-yy

I am able to select records and all like in the query below but select statement is picking records randomly that were create after the specified date. I am curious if there is a way to order by this date in the format dd-MMM-yy like 05-MAY-21..
select * from Employees
where createDt >= '05-MAY-21'
order by CreateDt asc
This statement is not giving what I am hoping for, which is start with records from date 05, 06, etc..
thank you in advance.
date in the format dd-MMM-yy like 05-MAY-21
A DATE is a binary data-type and it does NOT have any format.
If you do:
select *
from Employees
where createDt >= DATE '2021-05-05'
order by CreateDt asc
Then it will sort the dates in ascending order by year then by month and then by day and then by hour and then by minute and then by second (yes, a DATE always has a time component).
If you do:
select *
from Employees
where createDt >= '05-MAY-21'
order by CreateDt asc
Then you are implicitly asking Oracle to convert the string to a DATE and your query is actually doing:
select *
from Employees
where createDt >= TO_DATE(
'05-MAY-21',
( SELECT value
FROM NLS_SESSION_PARAMETERS
WHERE parameter = 'NLS_DATE_FORMAT' )
)
order by CreateDt asc
If the NLS_DATE_FORMAT session parameter does not match the format of your string then you will get unexpected results.
If you want to order the dates alphabetically, rather than chronologically then convert them to a string in the ORDER BY clause:
select *
from Employees
where createDt >= DATE '2021-05-05'
order by TO_CHAR(CreateDt, 'DD-MON-YY') asc
However, I'm not sure hy you would want to do this.
WHERE clause seems to be wrong.
You said that createDt column's datatype is DATE. So, why are you comparing it to a string? Use date literal or TO_DATE function, don't rely on implicit datatype conversion.
select * from Employees
where createDt >= date '2021-05-05' -- No! '05-MAY-21'
order by CreateDt asc
If, on the other hand, you only think that column's datatype is DATE but is - actually - VARCHAR2, then you'll have to "convert" it to a valid date value using TO_DATE function with appropriate date format mask, hoping that all values in that column have the same, valid format:
select * from Employees
where to_date(createDt, 'dd-mmm-yy', 'nls_date_language = english') >= date '2021-05-05'
order by to_date(createDt, 'dd-mmm-yy', 'nls_date_language = english') asc
If none of above helps, please, post some sample data. That includes CREATE TABLE and INSERT INTO several sample rows.
It would really help if you showed what results you're getting and what are your desired results...
Anyway, I'm trying to guess basing on the question and your comments
Let's start with "... statement is picking records randomly ..."
and "This statement is not giving what I am hoping for, which is start with records from date 05, 06, etc.."
and later: "and the order in which the records are weird.. like dates in the order of 08, 11, 12, 24, 24, 27, 27, 31, etc "
OK, the order "08, 11, 12, 24, 24, 27, 27, 31, etc." does not seem random, does it? It is pretty much the order you requested - ascending.
I'm guessing that all those dates are in may, so you really are getting what you requested for: records from Employees table with createDt greater or equal to 5th of may and sorted in ascending order.
But you are saying that the dates order seems weird to you, you were expecting 05, 06, ..., and you got 08, 11, 12, 24
Oh! What seems to be bothering you is the fact that you have some gaps between the dates and that the dates do not start with May 5th. Is that it? Well, then, simple answer is: those dates are simply not present in the Employees table.
You ask elsewhere ".. how do I limit that to send only 10 records?"
Ok, so let me guess what you would like to get.
You would like to see 10 consecutive dates starting with may 5th and the records which were created for each date.
In that case you have to "generate" those dates and then join them with your Employees table taking into account that for some of the dates you will have no row - hence LEFT JOIN.
with dates as
(select date '2021-05-05' + (level - 1) d from dual connect by level <= 10)
select e.*
from dates d
left join employees e
on e.createDt = d.d
order by d.d
or, if the createDt contains time component
with dates as
(select date '2021-05-05' + (level - 1) d from dual connect by level <= 10)
select e.*
from dates d
left join employees e
on e.createDt >= d.d and e.createDt < d.d + 1
order by d.d, e.createDt
I'm not sure if this is it, though
Also: you may use the literal '05-MAY-21' but only if you are absolutely sure that your session uses this exact date format. It's safer to use the datetime literal or to_date function ( to_date('05-MAY-21', 'DD-MON-YY') )

How do I separate the time and date in SQL navigator?

I am trying to separate the time and date in one column to be independent off each other. I am new at writing scripts
this is my query:
select
*
from
[tablename]
where
to_date([column_name]) in ( '15-Jun-2021', '16-Jun-2021' )
and
to_char([column_name],'dd-Mon-yyyy HH:MM:ss') < '15-Jun-2021 19:54:30'
The way you put it, it would be
select *
from your_table
where date_column >= date '2021-06-15'
and date_column < to_date('15.06.2021 19:54:30', 'dd.mm.yyyy hh24:mi:ss')
because
date_column should be of date datatype. If it isn't, you'll have problems of many kinds in the future. Therefore,
don't to_date it, it is already a date
don't to_char it either, because you'd be comparing strings and get unexpected result. Use that function when you want to nicely display the result
the second condition you wrote makes the first one questionable. If date_column is less than value you wrote, then you can omit date '2021-06-16' from the first condition because you won't get any rows for that date anyway
date literal (date '2021-06-15') sets time to midnight, so condition I wrote should return rows you want
SQL> select date '2021-06-15' first,
2 to_date('15.06.2021 19:54:30', 'dd.mm.yyyy hh24:mi:ss') second
3 from dual;
FIRST SECOND
------------------- -------------------
15.06.2021 00:00:00 15.06.2021 19:54:30
SQL>

Oracle get all records between current day inserted with timestamp

Unable to retrieve records properly with the formatted date values, need select query with right date format to get all records inserted per day
I have a date string like this in my script -  
dateString :='26-MAR-20 05.00.00.00000000 AM';
I want to add 0.313 minutes to this date value. And also I want to
increment it to the next day something like this- '27-MAR-20
05.00.00.00000000 AM'; ​
I tried this 
dateString :='26-MAR-20 05.00.00.00000000 AM';
dateString :=to_char(dateField,'DD-MON-YYYY HH24:MI:SS.FF');
dateField := to_timestamp(dateString, 'DD/MON/YYYY HH24:MI:SS.FF') + 0.313 * INTERVAL '1' MINUTE;
​
I can see the output and inserted these values into the DB but
unable to retrieve records properly with these formatted date values..
I suspect it might be to do with the timestamp fields
When I run this query -
select *from rptallexceptions where exceptiontime between '27-MAR-2020 04.00.00.000000000 AM' and '28-MAR-2020 03.59.00.000000000 AM' order by exceptiontime desc;
​
--- this one gives 3833 records but expected is 4600
it shows only the records on 27th march., records inserted with timestamp containing 28th march are not retrieved..
select *from rptallexceptions where exceptiontime between '28-MAR-2020 04.00.00.000000000 AM' and '29-MAR-2020 03.59.00.000000000 AM' order by exceptiontime desc;
--- this one returns '0' rows
Excerpt from the script:
cnt :=cnt +1;
dateString :=to_char(dateField,'DD-MON-YYYY HH24:MI:SS.FF');
-- add time difference for each exception.
dateField := to_timestamp(dateString, 'DD/MON/YYYY HH24:MI:SS.FF') + 0.313 * INTERVAL '1' MINUTE;
-- after n*4600 exceptions,update date to next date.
IF REMAINDER(cnt,exceptionsPerDay) = 0 THEN
dateField := to_timestamp(dateField + 1,'DD/MON/YYYY HH24:MI:SS.FF');
END IF;
The way I see it, your problem is that you're comparing timestamps to strings.
Sample table:
SQL> create table test as
2 select to_timestamp('27.03.2020 04:00:00:000000', 'dd.mm.yyyy hh24:mi:ss:ff6') datum, 'A' name from dual
3 union all
4 select to_timestamp('28.03.2020 15:23:00:000000', 'dd.mm.yyyy hh24:mi:ss:ff6') datum, 'A' name from dual;
Table created.
Query should use timestamp, not string:
SQL> select *
2 from test
3 where datum between to_timestamp('25.03.2020 12:00:00:000000am', 'dd.mm.yyyy hh:mi:ss:ff6am') --> timestamp
4 and to_timestamp('28.03.2020 12:00:00:000000am', 'dd.mm.yyyy hh:mi:ss:ff6am'); --> timestamp
DATUM N
--------------------------------------------------------------------------- -
27.03.20 04:00:00,000000000 A
SQL>
You were kind of "lucky" (though, one might call it bad luck as your query didn't fail, but it produced wrong results, according to number of rows being returned) for not getting an error as my database raises (due to different NLS settings):
SQL> select *
2 from test
3 where datum between '25.03.2020 12:00:00:000000am' --> string
4 and '28.03.2020 12:00:00:000000am'; --> string
where datum between '25.03.2020 12:00:00:000000am'
*
ERROR at line 3:
ORA-01830: date format picture ends before converting entire input string
SQL>
Strings are handled differently than dates (or timestamps), or numbers. Maybe you'll see the difference clearly in this example:
SQL> with test (col) as
2 (select '1' from dual union all
3 select '9' from dual union all
4 select '20' from dual
5 )
6 select *
7 from test
8 where col < '9';
CO
--
1
20
SQL>
20 < 9? Yes, if those are strings. The same might have happened to you. Try with proper datatype.
When you do:
dateString :=to_char(dateField,'DD-MON-YYYY HH24:MI:SS.FF');
you are converting whatever is the initial value of dateField to a string; but it looks like that has has not been set yet, so you end up with an empty string (which is the same as null). Your original value of dateString is never being used.
If you skip that and just do:
dateString :='26-MAR-20 05.00.00.00000000 AM';
dateField := to_timestamp(dateString, 'DD/MON/YYYY HH24:MI:SS.FF') + 0.313 * INTERVAL '1' MINUTE;
​then you are converting the 2-digit year 20 with a YYYY format element, which will turn it into year 0020, not 2020; but you also don't have AM in the format model, so it will get ORA-01830 anyway, and have HH24 instead of HH. I think your to_char() is attempting to 'correct' the string format, but that's not a great approach.
If you can't control the starting string format then the model has to match that:
dateString :='26-MAR-20 05.00.00.00000000 AM';
dateField := to_timestamp(dateString, 'DD-MON-RR HH:MI:SS.FF AM') + 0.313 * INTERVAL '1' MINUTE;
which gives dateField a value of 2020-03-26 05:00:18.780000.
Later on you do:
dateField := to_timestamp(dateField + 1,'DD/MON/YYYY HH24:MI:SS.FF');
which is also doing an implicit conversion of dateField + 1 - which is converted to a date, incidentally as timestamp + number is a date, not a timestamp - to a string; which will use your NLS_DATE_FORMAT. Presumably that is something like 'DD-MON-YYYY'.
So stepping through that:
dateField + 1 => date '2020-03-27 05:00:18' (losing fractional seconds)
implicit to_char(dateField + 1) => string '27-Mar-2020' (losing time)
to_timestamp(to_char(dateField + 1), '...') => timestamp '2020-03-27 00:00:00.000000' (with time at midnight).
So your between is, at best going to find records between 2020-03-26 05:00:18.780000 and 2020-03-27 00:00:00.0000000 - so it won't pick up any records later than midnight on the 27th.
Don't convert to and from strings when you don't need to; leave data in its native data type (timestamp in this case) and use direct manipulation with intervals.
Using between isn't ideal anyway, because it is inclusive; it would be better to end up with:
exceptiontime >= timestamp '2020-03-26 05:00:18.780000'
and exceptionTime < timestamp '2020-03-27 05:00:18.780000'

How to generate diff between TIMESTAMP and DATE in SELECT in oracle 10

I need to query 2 tables, one contains a TIMESTAMP(6) column, other contains a DATE column. I want to write a select statement that prints both values and diff between these two in third column.
SB_BATCH.B_CREATE_DT - timestamp
SB_MESSAGE.M_START_TIME - date
SELECT SB_BATCH.B_UID, SB_BATCH.B_CREATE_DT, SB_MESSAGE.M_START_TIME,
to_date(to_char(SB_BATCH.B_CREATE_DT), 'DD-MON-RR HH24:MI:SS') as time_in_minutes
FROM SB_BATCH, SB_MESSAGE
WHERE
SB_BATCH.B_UID = SB_MESSAGE.M_B_UID;
Result:
Error report -
SQL Error: ORA-01830: date format picture ends before converting entire input string
01830. 00000 - "date format picture ends before converting entire input string"
You can subtract two timestamps to get an INTERVAL DAY TO SECOND, from which you calculate how many minutes elapsed between the two timestamps. In order to convert SB_MESSAGE.M_START_TIME to a timestamp you can use CAST.
Note that I have also removed your implicit table join with an explicit INNER JOIN, moving the join condition to the ON clause.
SELECT t.B_UID,
t.B_CREATE_DT,
t.M_START_TIME,
EXTRACT(DAY FROM t.diff)*24*60 +
EXTRACT(HOUR FROM t.diff)*60 +
EXTRACT(MINUTE FROM t.diff) +
ROUND(EXTRACT(SECOND FROM t.diff) / 60.0) AS diff_in_minutes
FROM
(
SELECT SB_BATCH.B_UID,
SB_BATCH.B_CREATE_DT,
SB_MESSAGE.M_START_TIME,
SB_BATCH.B_CREATE_DT - CAST(SB_MESSAGE.M_START_TIME AS TIMESTAMP) AS diff
FROM SB_BATCH
INNER JOIN SB_MESSAGE
ON SB_BATCH.B_UID = SB_MESSAGE.M_B_UID
) t
Convert the timestamp to a date using cast(... as date). Then take the difference between the dates, which is a number - expressed in days, so if you want it in minutes, multiply by 24*60. Then round the result as needed. I made up a small example below to isolate just the steps needed to answer your question. (Note that your query has many other problems, for example you didn't actually take a difference of anything anywhere. If you need help with your query in general, please post it as a separate question.)
select ts, dt, round( (sysdate - cast(ts as date))*24*60, 2) as time_diff_in_minutes
from (select to_timestamp('2016-08-23 03:22:44.734000', 'yyyy-mm-dd hh24:mi:ss.ff') as ts,
sysdate as dt from dual )
;
TS DT TIME_DIFF_IN_MINUTES
-------------------------------- ------------------- --------------------
2016-08-23 03:22:44.734000000 2016-08-23 08:09:15 286.52

Get days between tow dates

Have a date in the table with format 10/04/14
then use this to_char(nameofcolumn, 'yyyymmdd')) to get this
20140410
Now, need rest a this value the sydate
I use this
select nameofcolumn,
to_char(nameofcolumn, 'yyyymmdd')) - to_char(sysdate, 'yyyymmdd') AS days
from
table;
But te result for example 07/07/14 return 300 days when is 90
Why are you converting these into strings? You can perform basic date arithmetic without converting them:
select nameofcolumn,
nameofcolumn - sysdate AS days
from
table;
I can't add comments so i will post a little addition as a separate answer.
SELECT to_date('07/07/2014','dd/mm/yyyy') - SYSDATE AS days FROM dual;
would return a number with a floating point instead of an integer. You've got to do either
SELECT trunc(to_date('07/07/2014','dd/mm/yyyy') - SYSDATE) AS days FROM dual;
or
SELECT to_date('07/07/2014','dd/mm/yyyy') - trunc(SYSDATE) AS days FROM dual;
depeding on what exactly you want to achieve.

Resources