Oracle get all records between current day inserted with timestamp - oracle

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'

Related

Oracle SQL Developer get table rows older than n months

In Oracle SQL Developer, I have a table called t1 who have two columns col1 defined as NUMBER(19,0) and col2 defined as TIMESTAMP(3).
I have these rows
col1 col2
1 03/01/22 12:00:00,000000000
2 03/01/22 13:00:00,000000000
3 26/11/21 10:27:11,750000000
4 26/11/21 10:27:59,606000000
5 16/12/21 11:47:04,105000000
6 16/12/21 12:29:27,101000000
My sysdate looks like this:
select sysdate from dual;
SYSDATE
03/03/22
I want to create a stored procedure (SP) which will delete rows older than 2 months and displayed message n rows are deleted
But when i execute this statement
select * from t1 where to_date(TRUNC(col2), 'DD/MM/YY') < add_months(sysdate, -2);
I don't get the first 2 rows of my t1 table. I get more than 2 rows
1 03/01/22 12:00:00,000000000
2 03/01/22 13:00:00,000000000
How can i get these rows and deleted it please ?
In Oracle, a DATE data type is a binary data type consisting of 7 bytes (century, year-of-century, month, day, hour, minute and second). It ALWAYS has all of those components and it is NEVER stored with a particular formatting (such as DD/MM/RR).
Your client application (i.e. SQL Developer) may choose to DISPLAY the binary DATE value in a human readable manner by formatting it as DD/MM/RR but that is a function of the client application you are using and not the database.
When you show the entire value:
SELECT TO_CHAR(ADD_MONTHS(sysdate, -2), 'YYYY-MM-DD HH24:MI:SS') AS dt FROM DUAL;
Then it outputs (depending on time zone):
DT
2022-01-03 10:11:28
If you compare that to your values then you can see that 2022-01-03 12:00:00 is not "more than 2 months ago" so it will not be matched.
What you appear to want is not "more than 2 months ago" but "equal to or more than 2 months, ignoring the time component, ago"; which you can get using:
SELECT *
FROM t1
WHERE col2 < add_months(TRUNC(sysdate), -2) + INTERVAL '1' DAY;
or
SELECT *
FROM t1
WHERE TRUNC(col2) <= add_months(TRUNC(sysdate), -2);
(Note: the first query would use an index on col2 but the second query would not; it would require a function-based index on TRUNC(col2) instead.)
Also, don't use TO_DATE on a column that is already a DATE or TIMESTAMP data type. TO_DATE takes a string as the first argument and not a DATE or TIMESTAMP so Oracle will perform an implicit conversion using TO_CHAR and if the format models do not match then you will introduce errors (and since any user can set their own date format in their session parameters at any time then you may get errors for one user that are not present for other users and is very hard to debug).
db<>fiddle here
Perhaps just:
select *
from t1
where col2 < add_months(sysdate, -2);

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>

How do I update the time in oracle sql?

I need a query to update a time in an appointment date by keeping the date but changing the time.
For example
10-Feb-2016 09:00:00
and i want to change it to 10-Feb-2016 10:00:00.
Update Appointment
set vdate = '10:00:00'
where vdate= '10-Feb-2016'
I get the "0 row has been updated'. Not sure if i'm missing something.
Thanks in advance.
You can use trunc() which sets the time part of a DATE (or TIMESTAMP) to 00:00:00, then add the 10 hours to it:
Update Appointment
set vdate = trunc(vdate) + interval '10' hour
where trunc(vdate) = DATE '2016-02-10'
This would change all rows that have a date 2016-02-10. If you only want to do that for those that are at 09:00 (ignoring the minutes and seconds) then just add one hour to those rows
Update Appointment
set vdate = trunc(vdate) + interval '1' hour
where trunc(vdate, 'hh24') = timestamp '2016-02-10 09:00:00'
trunc(vdate, 'hh24') will set the minutes and seconds of the date value to 00:00, so that the comparison with 2016-02-10 09:00:00 works properly.
Unrelated, but: do not rely on implicit data type conversion. '10-Feb-2016' is a string value, not a DATE literal. To specify a date either use an ANSI DATE literal (as I have done in the above statement) or use the to_date() function with a format mask to convert a string literal to a proper date value.
Your statement is subject to the evil implicit data type conversion and will fail if the SQL client running the statement uses a different NLS setting (it will fail on my computer for example)
If what you want to do is add an hour to a date, then you can do:
Update Appointment
set vdate = vdate + 1/24
where vdate = to_date('10/02/2016 09:00', 'dd/mm/yyyy hh24:mi');
since in Oracle, date differences are measured in number of days, and an hour is 1/24th of a day.
If what you want to do is specify an exact time (e.g. to 10:25:48), then you could do the following instead:
Update Appointment
set vdate = trunc(vdate) + 10/24 + 25/(24*60) + 48/(24*60*60)
where vdate = to_date('10/02/2016 09:00', 'dd/mm/yyyy hh24:mi');
Bear in mind that these updates will update all rows that have a date of 10th Feb 2016 at 9am. You'd need to change your query's where clause if you wanted to specify a more specific row or set of rows.
Try like this.
UPDATE MyTable
SET MyDate = DATEADD(HOUR, 4, CAST(CAST(MyDate AS DATE) AS DATETIME))
or
UPDATE MyTable
SET MyDate = DATEADD(HOUR, 4, CAST(FLOOR(CAST(MyDate AS FLOAT)) AS DATETIME))

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

How to Add an Interval to a Date in Oracle

I'm trying to get a new date from the product of 'date' + 'time interval'.
Something like this.
'15/02/2016 18:00:00' + '+00 02:00:00.000000'
Expected result:
'15/02/2016 20:00:00'
But using the columns in database.
CREATE TABLE timerest
(
DATE_ASIGN DATE,
TIME_ASIGN INTERVAL DAY(2) TO SECOND(0)
);
Thanks for your help.
You can just add them together:
insert into timerest (date_asign, time_asign)
values (to_date('15/02/2016 18:00:00', 'DD/MM/YYYY HH24:MI:SS'),
to_dsinterval('+00 02:00:00.000000'));
alter session set NLS_DATE_FORMAT = 'DD/MM/YYYY HH24:MI:SS';
select date_asign + time_asign from timerest;
DATE_ASIGN+TIME_ASIGN
---------------------
15/02/2016 20:00:00
This follows the rules for datetime/interval arithmetic: date + interval = date.
If you have a date in DATE format you can simply add the a numeric interval that represents days (for example 1.5 is 1 day and a half)
You can extract from the time interval days and hours and then add them to to you date because, if I remember correctly, you can't add directly them to a date type (maybe to a timestamp type you can)
To extract the days you can use the extract function:
(
extract(second from TIME_ASIGN)/3600)+(extract(hour from TIME_ASIGN)/24)+(extract(day from TIME_ASIGN)/24)
then you add the number to your DATE_ASIGN

Resources