How to pass timestamp value in Oracle Stored Procedure? - sql

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

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 can I change the date component of an Oracle timestamp

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'

to_Char(DATE) with time

I have a DB (Oracle Database 11g Enterprise Edition Release 11.2.0.4.0 - 64bit Production). In there is a table (Course) in which I have a Date column (start_dte). I want to format the output to a char so I used:
SELECT start_dte,
to_Char(start_dte) AS start_dte_2,
to_Char(start_dte,'DD.MM.YYYY') AS start_dte_3,
to_Char(start_dte,'DD.MM.YYYY HH24:MI:SS') AS start_dte_4,
to_Char(start_dte,'DD.MM.YYYY HH12:MI:SS') AS start_dte_5,
to_Char(start_dte,'DD.MM.YYYY HH.MI.SSXFF AM') AS start_dte_6,
to_Char(start_dte,'DD.MM.YYYY HH24:MI:SSxFF') AS start_dte_7,
to_Char(start_dte,'DD-MON-YYYY HH24:MI:SSxFF TZH:TZM') AS start_dte_8
FROM Course
The Results:
Number 6,7,8 give me
java.sql.SQLException: ORA-01821: date format not recognized
6 has the same format as:
SELECT value
FROM V$NLS_PARAMETERS
WHERE parameter = 'NLS_TIME_FORMAT'
7 and 8 are taken from the Oracle documentation but this is only for timestamps. So what i am missing? I know the actual datatype should be datetime but I only have read access to that DB. What i would actually like:
Or even better:
A DATE doesn't have any time zone information, so if you want to adjust the displayed time then you'll need to specify which time zone that date nominally represents, and which time zone you want to convert it to.
An an example, if your stored date represents UTC and you want to see the equivalent local time in Paris, you can state that the stored time is UTC by casting it as a plain timestamp and passing that into the from_tz() function; and then specify the target timezone with at:
-- CTE for your sample data
with course (start_dte) as (
select cast(timestamp '2018-10-17 14:00:00' as date) from dual
)
-- actual query
select from_tz(cast(start_dte as timestamp), 'UTC')
at time zone 'Europe/Paris' as start_timestamp_tz
from course;
START_TIMESTAMP_TZ
------------------------------------------
2018-10-17 16:00:00.000000000 EUROPE/PARIS
Since you're querying this via JDBC you probably want to retrieve that as its native data type, and then choose how to display it locally. (But you could presumably query it as a date and adjust it in Java too...)
If you want to convert it to a string on the DB side, though, just use the format model you already used:
select to_char(from_tz(cast(start_dte as timestamp), 'UTC')
at time zone 'Europe/Paris', 'DD.MM.YYYY HH24:MI') as start_date
from course;
START_DATE
----------------
17.10.2018 16:00
Getting it into two fields is also simple, and if you don't want to repeat the conversion you can use a CTE or an inline view:
select to_char(start_timestamp_tz, 'DD.MM.YYYY') as start_date,
to_char(start_timestamp_tz, 'HH24:MI') as start_time
from (
select from_tz(cast(start_dte as timestamp), 'UTC')
at time zone 'Europe/Paris' as start_timestamp_tz
from course
);
START_DATE START_TIME
---------- ----------
17.10.2018 16:00
But it sounds like Java should be doing that conversion to strings for display.
What i would actually like:
| START_DATE |
|------------------|
| 17.10.2018 16:00 |
Or even better:
| START_DATE | START_TIME |
|------------|------------|
| 17.10.2018 | 16:00 |
Use:
SELECT to_Char(start_dte,'DD.MM.YYYY') AS start_date,
to_Char(start_dte,'HH24:MI') AS start_time
FROM Course
If your data is stored in the table in one time zone (i.e. UTC) and you want it in another time zone then use:
CAST( date_value AS TIMESTAMP ) to convert it from a DATE data type to a TIMESTAMP data type.
FROM_TZ( timestamp_value, timezone_string ) to convert it from a TIMESTAMP data type to a TIMESTAMP WITH TIME ZONE data type at the given time zone.
timestamp_with_timezone_value AT TIME ZONE timezone_string to convert it from one time zone to another time zone.
Like this:
SELECT TO_CHAR(
FROM_TZ( CAST( start_dte AS TIMESTAMP ), 'UTC' ) AT TIME ZONE 'Europe/Berlin',
'DD.MM.YYYY'
) AS start_date,
TO_CHAR(
FROM_TZ( CAST( start_dte AS TIMESTAMP ), 'UTC' ) AT TIME ZONE 'Europe/Berlin',
'HH24:MI'
) AS start_time
FROM Course
start_dte is a DATE value.
DATE does neither have fractional seconds (i.e. XFF) nor time zone information (i.e. TZH:TZM)
Use TIMESTAMP WITH TIME ZONE is you like to get such output.

PLSQL - how to extract month and year only from full date

I have a date like 01/03/2016. i want to get back 03/2016 in date type.
I know extract function, but it give me char type.
I don't want to use to_char.
Is there any way to do that?
Date datatype always has a day, month, year, hour, minute and second part.
You can use use to_char() function to extract the required component from a date.
select to_char(sysdate,'MM/YYYY') from dual;
However you can use below query but it will return 01-FEB-16
select to_date('01-2016','DD\YYYY') from dual;
Date does not have a format - it is represented internally by a series of bytes which you can see using:
SELECT DUMP( SYSDATE ) FROM DUAL;
Which outputs something like ( for the date 2016-02-17T09:13:44Z):
Typ=13 Len=8: 224,7,2,17,9,13,44,0
If you want the year & month then you can do use TRUNC( date_value, 'MM' ) or TO_CHAR( date_value, 'MM/YYYY' ):
SELECT TRUNC( SYSDATE, 'MM' ) AS "Date",
TO_CHAR( SYSDATE, 'MM/YYYY' ) AS monthyear,
DUMP( TRUNC( SYSDATE, 'MM' ) ) AS "Bytes"
FROM DUAL
Which outputs (note, the last 4 bytes are all zero after truncation):
Date | MonthYear | Bytes
--------------------------------------------------------------------
2016-02-01T00:00:00Z | 02/2016 | Typ-13 Len=8: 224,7,2,1,0,0,0,0
However, the output of the date is based on the NLS_DATE_FORMAT session parameter and your client will do an implicit TO_CHAR when it outputs the date using this format mask.
You can find out your current NLS_DATE_FORMAT with the query (as you can see above, mine is set to an ISO8601 format YYYY-MM-DD"T"HH24:MI:SS"Z"):
SELECT VALUE
FROM NLS_SESSION_PARAMETERS
WHERE PARAMETER = 'NLS_DATE_FORMAT';
You can then alter it using:
ALTER SESSION SET NLS_DATE_FORMAT = 'MM/YYYY';
Then when you do:
SELECT SYSDATE, DUMP( SYSDATE ) FROM DUAL;
You will get the output:
Date | Bytes
-----------------------------------------------
02/2016 | Typ=13 Len=8: 224,7,2,17,9,13,44,0
Note, the last 4 bytes are not all non-zero (the date has not been truncated) but now the output is just the month/year.
However, it is just simpler to store the date as a date (with the day and time components unchanged) and then use TO_CHAR( date_value, 'MM/YYYY' ) whenever you want to output it as you will get the output format you want without changing the value across the entire session.

Get today's date - Different timezone

I just wanted to know whether the following SQL is good to convert US server time to Thailand date. As we have 12 hours difference in the time, and TH time is ahead of U.S time
SELECT TO_DATE(
TO_CHAR(
SYSTIMESTAMP AT TIME ZONE 'Asia/Bangkok', 'yyyy-mm-dd',
'NLS_DATE_LANGUAGE = american'), 'yyyy-mm-dd') AS TODAY
FROM dual;
It works perfectly fine. But are there any other better way to convert timestamp of server from one timezone to another as I need to compare today's date based on this result in my outer SQL.
You could CAST the timestamp to your desired timezone.
For example,
SQL> WITH data AS (
2 SELECT SYSTIMESTAMP AT TIME ZONE 'Asia/Bangkok' tm_bangkok FROM dual
3 )
4 SELECT tm_bangkok,
5 CAST(tm_bangkok AT TIME ZONE 'EST' AS TIMESTAMP) tm_est
6 FROM data;
TM_BANGKOK TM_EST
--------------------------------------------- ----------------------------
03-NOV-15 12.54.18.951000 PM ASIA/BANGKOK 03-NOV-15 12.54.18.951000 AM
There is no reason to cast a TIMESTAMP to CHAR and then back to TIMESTAMP again. Simply do
SELECT SYSTIMESTAMP AT TIME ZONE 'Asia/Bangkok' AS TODAY
FROM dual;