How can I change the date component of an Oracle timestamp - sql

I have an Oracle TIMESTAMP column, which contains values such as:
2019-11-05 15:16:31
I would like to update these rows to change the date component from 5th November 2019 to 8th June 2020, without changing the time component. I understand that underneath this is just a numeric value, with no separation of year/month/day/hour/minute/second/etc. The aim is that the above value becomes:
2020-06-08 15:16:31
The best I can come up with is:
update mytable set tscolumn = tscolumn + 216
(with appropriate where clause of course)
which works but isn't particularly pretty and relies on TimeAndDate.com

Use a TIMESTAMP literal where the time is set to midnight and then add the time component from your row, which you can work out by subtracting the TRUNCated column value from the non-truncated column value:
UPDATE mytable
SET tscolumn = TIMESTAMP '2020-06-08 00:00:00'
+ ( tscolumn - TRUNC( tscolumn ) ) DAY TO SECOND;
(DAY TO SECOND is included so that the subtraction is done as an INTERVAL data type rather than Oracle's default of a numeric difference.)
Which, for the sample data:
CREATE TABLE mytable ( tscolumn ) AS
SELECT TIMESTAMP '2019-11-05 15:16:31' FROM DUAL;
Outputs:
| TSCOLUMN |
| :---------------------------- |
| 2020-06-08 15:16:31.000000000 |
db<>fiddle here

One option uses date artithmetics:
update mytable
set tscolumn = tscolumn + (date '2020-06-08' - date '2019-11-05')
where tscolumn >= date '2019-11-05' and tscolumn < date '2019-11-06'
The substraction between the literal dates gives you the number of days in between.
Note that I added a where clause to the query so it only applies on columns whose date part is '2019-11-05' - it is unclear what you want to do with other dates.
If you really have a timestamp column and you want to preserve the fractional seconds, then you want to use intervals:
update mytable
set tscolumn = tscolumn + (date '2020-06-08' - date '2019-11-05') * interval '1' day
where tscolumn >= date '2019-11-05' and tscolumn < date '2019-11-06'

Related

How to get 24 hours back time from current time in Oracle

I have a column in my Oracle db which records the creation time of a user in the following format
30-NOV-20 11.49.11.000000000 AM (TIMESTAMP(6) format).
What I wanted to do is select all records whose creation time is 24 hours earlier than current time
So what I was going to do was subtract 1 from current time and compare it. But when I subtract 1 it returns only the date.
select * from user where created_date < SYSTIMESTAMP-1
dbms_output.put_line (SYSTIMESTAMP-1);-->29-NOV-20
The time parts are missing which makes me unable to compare with created time in the table
Please help me to complete this task.
If you subtract 1 from a date datatype (e.g. sysdate), it'll move you back one day. But, if you subtract it from a timestamp datatype value, Oracle will convert it to date and return a date (moreover, it'll be truncated).
See the following example:
SQL> select
2 systimestamp val1,
3 systimestamp - 1 val2,
4 --
5 systimestamp - interval '1' day val3
6 from dual;
VAL1
-----------------------------------------------------
VAL2
--------
VAL3
-----------------------------------------------------
30.11.20 09:55:01,439352 +01:00
29.11.20
29.11.20 09:55:01,439352000 +01:00
SQL>
So, what you should do is to subtract an interval, i.e.
select *
from user
where created_date < systimestamp - interval '1' day;
Half the problem is that whatever client program is being used to display the values is using the default date format for their territory and that default format is set to DD-MON-RR.
You can change the NLS_DATE_FORMAT and NLS_TIMESTAMP_FORMAT session parameters and that will (assuming your client program uses them and not some internal settings) give you the output you are expecting:
ALTER SESSION SET NLS_DATE_FORMAT = 'YYYY-MM-DD HH24:MI:SS';
ALTER SESSION SET NLS_TIMESTAMP_FORMAT = 'YYYY-MM-DD HH24:MI:SS.FF9';
ALTER SESSION SET NLS_TIMESTAMP_TZ_FORMAT = 'YYYY-MM-DD HH24:MI:SS.FF9TZR';
Then
SELECT SYSDATE, SYSTIMESTAMP FROM DUAL;
Outputs (depending on your system time zone):
SYSDATE | SYSTIMESTAMP
:------------------ | :----------------------------------
2020-11-30 10:12:22 | 2020-11-30 10:12:22.476282000+00:00
If you use SYSTIMESTAMP-1 then Oracle does not support subtracting a NUMBER data type from a TIMESTAMP [WITH TIME ZONE] data type but it does support subtracting a NUMBER data type from a DATE data type and will perform an implicit cast from TIMESTAMP to DATE so that the query is valid.
For example:
SELECT SYSDATE - 1, SYSTIMESTAMP - 1, SYSTIMESTAMP - INTERVAL '1' DAY FROM DUAL;
Outputs:
SYSDATE-1 | SYSTIMESTAMP-1 | SYSTIMESTAMP-INTERVAL'1'DAY
:------------------ | :------------------ | :----------------------------------
2020-11-29 10:24:02 | 2020-11-29 10:24:02 | 2020-11-29 10:24:02.651735000+00:00
You can see that in the middle column SYSTIMESTAMP-1 gives the same output as SYSDATE-1 but in the right-hand column, subtracting an interval has ensured that TIMESTAMP WITH TIME ZONE data type is maintained.
So your query:
SELECT * FROM user WHERE created_date < SYSTIMESTAMP-1
Is effectively:
SELECT * FROM user WHERE created_date < CAST( SYSTIMESTAMP AS DATE )-1
Which will have exactly the same year, month, day, hour, minute and (integer) second components but will lose the fractional seconds and time zone information from the SYSTIMESTAMP.
If your column does not have time zone data and the level of precision in the fractional seconds does not matter to you then your query will work adequately.
However, if you want to keep the time zone and/or fractional seconds information then you can use:
SELECT * FROM user WHERE created_date < SYSTIMESTAMP - INTERVAL '1' DAY;
However, if created_date is a DATE column, you probably want:
SELECT * FROM user WHERE created_date < SYSDATE - INTERVAL '1' DAY;
or
SELECT * FROM user WHERE created_date < SYSDATE - 1;
db<>fiddle here
You can use it as (SYSDATE, -1).
Also, if you want to search for new records within 24 hours, it should be "created_date> = (sysdate-1)".

How to pass timestamp value in Oracle Stored Procedure?

I have a table column which has timestamp data type. How to pass timestamp value to the table column? I am passing to_timestamp('1240','HH:MI:SS') in oracle procedure. In DB table I have a value like 01-AUG-20 12.40.00.000000000 PM . I only want to store timestamp in the DB table. Is there a way? Please guide me on this. Thanks
You appear to be confused about what the values store. A TIMESTAMP data type stores a date and time with year, month, day, hour, minute, second and optional fractional seconds and time zone components; you cannot have a TIMESTAMP with just hour, minute and second components as it will always have year-day components.
If you want to store time then either:
Use an INTERVAL DAY TO SECOND data type (with zero days);
Use a DATE (or, if you want fractional seconds, TIMESTAMP) data type and set the year-day components to a fixed value (or ignore them);
Use a string in a fixed format; or
Store the number of seconds after midnight and use TO_DATE( value, 'SSSSS' ) to convert to a date and then TO_CHAR to format it as needed.
I would say that if you want to add times then use an INTERVAL data type as it will natively support that.
For example:
CREATE TABLE times1 ( value INTERVAL DAY TO SECOND );
INSERT INTO times1 ( value ) VALUES ( INTERVAL '12:40' HOUR TO MINUTE );
SELECT * FROM times1;
Which outputs:
| VALUE |
| :------------------ |
| +00 12:40:00.000000 |
If you want to display times then use a DATE and ignore the year-to-day components as you can easily format the time using TO_CHAR.
For example:
ALTER SESSION SET NLS_DATE_FORMAT = 'YYYY-MM-DD HH24:MI:SS';
CREATE TABLE times2 ( value DATE );
INSERT INTO times2 ( value )
-- Fixed date
SELECT DATE '1970-01-01' + INTERVAL '12:40' HOUR TO MINUTE FROM DUAL UNION ALL
-- Today's date
SELECT TRUNC( SYSDATE ) + INTERVAL '12:40' HOUR TO MINUTE FROM DUAL UNION ALL
-- First of current month
SELECT TO_DATE( '12:40', 'HH24:MI' ) FROM DUAL;
SELECT value, TO_CHAR( value, 'HH24:MI' ) FROM times2;
Which outputs:
VALUE | TO_CHAR(VALUE,'HH24:MI')
:------------------ | :-----------------------
1970-01-01 12:40:00 | 12:40
2020-08-05 12:40:00 | 12:40
2020-08-01 12:40:00 | 12:40
db<>fiddle here

PostgreSQL - subtract 'days' from a returned 'date' value without also returning timestamp

I'm querying a table to get some date, like so:
SELECT date - INTERVAL '10 day' AS date
FROM example_table
WHERE username = 'Bob'
LIMIT 1;
The date column in the example_table does not have a timestamp. All dates in the column are stored in the following manner:
YYYY-MM-DD
The query above will return a result like so:
2016-11-20 00:00:00.000000
It takes the date found, goes back 10 days, and returns that date. But I want it to return the date without adding the timestamp, like so:
2016-11-20
If I use INTERVAL it always seems to add a timestamp. Is there a way to only get the date?
Your query is fine (but can be simplified, as demonstrated by a_horse_with_no_name). What you are seeing is a display issue. You can format your date to a string in the relevant format using to_char():
SELECT to_char("date" - INTERVAL '10 day', 'yyyy-mm-dd') AS "date"
FROM example_table
WHERE username = 'Bob'
LIMIT 1;
Note: LIMIT without an ORDER BY does not make sense: if there is more than one record in the resultset, you actually get a random record out of them.
You can use the interval notation and convert back to a date:
SELECT (date - INTERVAL '10 day')::date AS date
You can subtract (or add) an integer from a date. That integer represents the number of days:
SELECT "date" - 10 AS "date"
FROM example_table
WHERE username = 'Bob'
LIMIT 1;

Updating dates from 19XX to 20XX in Oracle

I have a table where dates have been incorrectly entered as 19XX rather than 20XX.
Is it possible to have an update query that will amend any value in a particular field from 19XX to 20XX (UK date format) where the original date is less than 01/01/2000?
For example
ID FieldA
123 23/11/1917
would become
ID FieldA
123 23/11/2017
Assuming that you may have correct date in the latter half of the 20th century and it is only the earlier half of the century you need to update (please confirm this) then:
UPDATE table_name
SET date_column = ADD_MONTHS( date_column, 12 * 100 )
WHERE date_column >= DATE '1900-01-01'
AND date_column < DATE '1950-01-01';
If you do want to change the dates for all years in the 20th century then:
UPDATE table_name
SET date_column = ADD_MONTHS( date_column, 12 * 100 )
WHERE date_column >= DATE '1900-01-01'
AND date_column < DATE '2000-01-01';
Note: you need to use the ADD_MONTHS function rather than adding INTERVAL '100' YEAR(3) since there are dates in the 20th century that are not in the 21st century (i.e. 1900-02-29).
You can take advantage of shortened YY date format. To quote the docs:
If you use the TO_DATE function with the YY datetime format element,
then the year returned always has the same first 2 digits as the
current year.
Therefore, to_date(to_char(fieldA, 'ddmmyy'), 'ddmmyy') should do what you need. Be careful though, if there are dates with years less than 1900 or over 2100, they'll be converted as well.
If FieldA is a date, then you can do:
select (case when fieldA < date '2000-01-01'
then fieldA + interval '100' year(3)
else fieldA
end)

Cast date to timestamp in pgSQL

Is there a way to cast a date to timestamp. For an example if I had a date like 2012/05/01, how can I convert it to a timestamp like 2012-05-01 00:00:01
You can convert it to a timestamp with this code:
SELECT current_date::timestamp
It will directly assign the time 00:00:00 to current_date.
You can cast the date column to text and then concatenate with the time portion of the timestamp. In the query below I create a timestamp from the current date.
select (cast(current_date as text) || ' 00:00:01'):: timestamp
from yourTable;
Or if we already have a date type, we can simply add on the time component:
select current_date + '00:00:01'::time
Output:
11.07.2017 00:00:01
Demo
Update:
If you just want the difference in months between two dates you can use the following:
DATE_PART('month', AGE(end_date, start_date))
Of course, there is no time component involved here, but assuming you were planning to assign the dummy 00:00:01 to both timestamps, the result would not change.
select cast(current_date as timestamp) + interval '1 second'
Close to Standard SQL, only the interval syntax differs interval '1' second.
You can add time part to date
select current_date + '00:00:01'::time