Migration of PIPELINED from Oracle to Postgres - sql

Creation of TYPE is as follows:
create or replace Type T_dates as OBJECT(
p_st_date VARCHAR2(32767),
p_en_date VARCHAR2(32767)
);
create or replace TYPE T_dates_tab AS table of T_dates;
Sample Code which needs to convert is as follows.
Oracle Code:
Function f_get_dates(p_st_date varchar2,
p_end_date varchar2,
p_st_time varchar2,
p_end_time varchar2)
return T_dates_tab PIPELINED
IS
V_DAY_COUNT NUMBER;
P_TEMP_ST_DATE VARCHAR2(100 ):=p_st_date;
BEGIN
SELECT TO_DATE(p_end_date,'MM-DD-YYYY ') - TO_DATE(p_temp_st_date,'MM-DD-YYYY ') INTO V_DAY_COUNT FROM DUAL ;
WHILE V_DAY_COUNT >= 0
LOOP
DBMS_OUTPUT.PUT_LINE(p_temp_st_date||' '||p_st_time||','||p_temp_st_date||' '||p_end_time);
PIPE ROW (T_dates((p_temp_st_date||' '||p_st_time),(p_temp_st_date||' '||p_end_time)));
SELECT TO_CHAR(TO_DATE(p_temp_st_date,'MM-DD-YYYY')+1,'MM/DD/YYYY') INTO p_temp_st_date FROM DUAL;
SELECT TO_DATE(p_end_date,'MM-DD-YYYY ') - TO_DATE(p_temp_st_date,'MM-DD-YYYY ') INTO V_DAY_COUNT FROM DUAL ;
END LOOP;
DBMS_OUTPUT.PUT_LINE('f_get_dates');
RETURN;
END f_get_dates;
Need Postgres Inputs for the above Oracle code.

This function can be massively simplified in Postgres. In fact you don't really need your own function for that:
select p_st_date::date + time '00:00:00' as p_st_date,
p_st_date::date + time '18:00:00' as p_en_date
from generate_series(date '2018-01-01', date '2018-01-30', interval '1' day) as t(p_st_date);
In contrast to your function this returns real timestamp values rather than character values. If you indeed want to treat timestamp values as character data, use to_char() to convert that.
You can put the above into a SQL function:
create function f_get_dates(p_st_date date, p_en_date date, p_st_time time, p_end_time time)
returns table (p_st_date timestamp, p_en_date timestamp)
as
$$
select p_st_date::date + p_st_time as p_st_date,
p_st_date::date + p_end_time as p_en_date
from generate_series(p_st_date, p_en_date, interval '1' day) as t(p_st_date);
$$
language sql;
If you really want to return the timestamps as character values, just format them using to_char():
create function f_get_dates(p_st_date date, p_en_date date, p_st_time time, p_end_time time)
returns table (p_st_date varchar, p_en_date varchar)
as
$$
select to_char(p_st_date::date + p_st_time, 'mm-dd-yyyy hh24:mi:ss') as p_st_date,
to_char(p_st_date::date + p_end_time, 'mm-dd-yyyy hh24:mi:ss') as p_en_date
from generate_series(p_st_date, p_en_date, interval '1' day) as t(p_st_date);
$$
language sql;

Related

PL/SQL function with date parameter returns wrong output

In order to make my main code more readable, I have wrapped an operation to convert a date to epoch format in a function. However, the function returns an incorrect value. What am I doing wrong?
What I get
Function
create or replace function date_to_epoch(
date_in in date)
return number
is
epoch_out number;
begin
epoch_out := round((to_date(date_in, 'yyyy-mm-dd hh24:mi:ss') - to_date('1970-01-01', 'yyyy-mm-dd'))*24*60*60);
return epoch_out;
end;
I call the function with:
declare
date_out number;
begin
select date_to_epoch(to_date('2020-01-01 00:00:00', 'yyyy-mm-dd hh24:mi:ss')) into date_out from dual;
DBMS_OUTPUT.PUT_LINE('Output:' || date_out);
end;
returns: -62134128000
What I expect
I get the expected output when I run the same operation in a simple SQL statement:
select round((to_date('2020-01-01', 'yyyy-mm-dd hh24:mi:ss') - to_date('1970-01-01', 'yyyy-mm-dd'))*24*60*60) from dual;
returns: 1577836800
Your input parameter is a date already, so you should not use to_date() on it. Bad things happen when you do that.
I would write your code as:
create or replace function date_to_epoch(date_in in date)
return number
is
epoch_out number;
begin
epoch_out := round((date_in - date '1970-01-01')*24*60*60);
return epoch_out;
end;
Note that you don't actually need an intermediate variable to store the return value of the function. You can directly do:
create or replace function date_to_epoch(date_in in date)
return number
is
begin
return round((date_in - date '1970-01-01')*24*60*60);
end;
In this demo on DB Fiddle, the code yields:
Output:1577836800
What am I doing wrong?
The TO_DATE function has the signature:
TO_DATE( date_string, format_model, nls_param )
Where all the arguments are expected to have a string data-type.
You are not passing a string as the first argument; you are passing a DATE data type. Oracle will try to be helpful and convert the DATE you are passing to a string by implicitly calling TO_CHAR using the current session's NLS date format session parameter.
So, your function is effectively:
create or replace function date_to_epoch(
date_in in date
) return number
is
epoch_out number;
begin
epoch_out := round(
( to_date(
TO_CHAR(
date_in,
( SELECT value
FROM NLS_SESSION_PARAMETERS
WHERE parameter = 'NLS_DATE_FORMAT' )
),
'yyyy-mm-dd hh24:mi:ss'
)
-
to_date(
'1970-01-01',
'yyyy-mm-dd'
)
)*24*60*60
);
return epoch_out;
end;
To fix it, you need to just use the date input as a date rather than trying to convert something that is already a date to a date:
CREATE FUNCTION date_to_epoch(
date_in IN DATE
) RETURN NUMBER DETERMINISTIC
IS
BEGIN
RETURN ROUND( ( date_in - DATE '1970-01-01' )*24*60*60 );
END date_to_epoch;
However, you also need to make sure that your dates are in the UTC time zone and if they aren't convert them to UTC before calculating the epoch:
CREATE FUNCTION date_to_epoch(
date_in IN DATE,
timezone IN VARCHAR2 DEFAULT 'UTC'
) RETURN NUMBER DETERMINISTIC
IS
BEGIN
RETURN ROUND(
( CAST(
FROM_TZ( date_in, timezone ) -- Convert to a timestamp in the correct TZ
AT TIME ZONE 'UTC' -- Change to the UTC TZ
AS DATE -- Cast back to a date
)
- DATE '1970-01-01'
)*24*60*60
);
END date_to_epoch;
/
So:
SELECT date_to_epoch( DATE '1970-01-01' ) AS utc_epoch,
date_to_epoch( DATE '1970-01-01', 'PST' ) AS pst_epoch
FROM DUAL;
outputs:
UTC_EPOCH | PST_EPOCH
--------: | --------:
0 | 28800
db<>fiddle here

How to extract year or month,day from oracle function without using sub-query?

I have the query below is not working for me,
this function from_tz(to_timestamp(START_TIME, 'YYYYMMDDHH24MISS'),
substr(UTC_TIME_CODE_OFFSET,1,3)||':'||'00') at local as local_tstz
allow me to convert timezones from diffrernt reguins to local,when I try to extract day from this function using TO_CHAR(from_tz(to_timestamp(START_TIME, 'YYYYMMDDHH24MISS'),
substr(UTC_TIME_CODE_OFFSET,1,3)||':'||'00') at local as local_tstz,'DD')
it's not possible because it's not a real column , I end up creating new sub-query,I'm ending up creating many sub queries and I want to avoid this because
it make the query complex and it make the query take more longer time,
select from_tz(to_timestamp(START_TIME, 'YYYYMMDDHH24MISS'),
substr(UTC_TIME_CODE_OFFSET,1,3)||':'||'00'),
from_tz(to_timestamp(START_TIME, 'YYYYMMDDHH24MISS'),
substr(UTC_TIME_CODE_OFFSET,1,3)||':'||'00') at local as local_tstz,
to_char(from_tz(to_timestamp(START_TIME, 'YYYYMMDDHH24MISS'),
substr(UTC_TIME_CODE_OFFSET,1,3)||':'||'00') at local as local_tstz,'DD')
from TAPIN_201906#billingdb;
I cannot reproduce your problem in version 19c, or else I don't understand it. Anyway, three remarks:
1) If your START_TIME is really a string, that is just not a good idea at all. Use real DATE or TIMESTAMP datatypes. In your shoes, I would replace both columns by one column with a TIMESTAMP WITH TIME ZONE datatype. As it is, you cannot sort properly by date because of the time zone offsets.
2) With your current data, you can simplify the conversion to a local timestamp, as I'll show in a minute.
3) As pointed out in a comment, EXTRACT starts from UTC time, not local time, so I changed my solution back to TO_CHAR.
with data(START_TIME, UTC_TIME_CODE_OFFSET) as (
select '20001112012345', '+02' from dual
)
select
to_timestamp_TZ(
START_TIME || substr(UTC_TIME_CODE_OFFSET,1,3),
'YYYYMMDDHH24MISSTZH'
) at local as local_ts,
to_char(
to_timestamp_TZ(
START_TIME || substr(UTC_TIME_CODE_OFFSET,1,3),
'YYYYMMDDHH24MISSTZH'
)
,'DD'
) as to_char_day,
extract(
day from
to_timestamp_TZ(
START_TIME || substr(UTC_TIME_CODE_OFFSET,1,3),
'YYYYMMDDHH24MISSTZH'
)
) as extract_day
from data;
LOCAL_TS TO_CHAR_DAY EXTRACT_DAY
2000-11-12 00:23:45,000000000 EUROPE/PARIS 12 11
Best regards,
Stew Ashton
Like Stew I don't understand your problem. Of course, the proper solution would be to use TIMESTAMP WITH TIME ZONE or TIMESTAMP WITH LOCAL TIME ZONE data type for your columns.
Otherwise you can use a function:
create or replace function TO_LOCAL(START_TIME IN DATE, UTC_TIME_CODE_OFFSET IN integer) return TIMESTAMP WITH TIME ZONE as
begin
RETURN from_tz(to_timestamp(START_TIME, 'YYYYMMDDHH24MISS'),
UTC_TIME_CODE_OFFSET||':'||'00') at local;
end TO_LOCAL;
or you can define a VIRTUAL column of such values.
Update:
It does not matter whether START_TIME and UTC_TIME_CODE_OFFSET are columns or functions, see this example:
create or replace function START_TIME AS NUMBER is
begin
return TO_CHAR(SYSDATE, 'YYYYMMDDHH24MISS');
end;
You can select such function in the same way as it would be column, there is no difference.
Please expand on your contention that 'I can't extract year or month,day ... because it's not a real column'. That seems as though you think the FROM parameter of the must be an actual table column. But this in not true. As long as the parameter satisfies the EXTRACT data requirement is can be used. For your case see below:
alter session set NLS_DATE_FORMAT = 'YYYY-MM-DD HH24:MI:SS';
declare
UTC_TIME_CODE_OFFSET varchar2(3) := '-05';
start_date varchar2(14);
function get_some_date
return varchar
as
begin
return to_char(sysdate + dbms_random.value(1000,10000), 'YYYYMMDDHH24MISS') ;
end get_some_date;
begin
for i in 1 .. 10
loop
start_date := get_some_date;
dbms_output.put( 'Date is ' || to_date(start_date, 'yyyy-mm-dd hh24:mi:ss'));
dbms_output.put( ' Extract month is ' || EXTRACT( month FROM (from_tz(to_timestamp( start_date, 'YYYYMMDDHH24MISS'), substr(UTC_TIME_CODE_OFFSET,1,3)||':'||'00'))) );
dbms_output.put( ' Extract year is ' || EXTRACT( year FROM (from_tz(to_timestamp( start_date, 'YYYYMMDDHH24MISS'), substr(UTC_TIME_CODE_OFFSET,1,3)||':'||'00'))) );
dbms_output.put( ' Extract day is ' || EXTRACT( day FROM (from_tz(to_timestamp( start_date, 'YYYYMMDDHH24MISS'), substr(UTC_TIME_CODE_OFFSET,1,3)||':'||'00'))) );
dbms_output.put_line('.');
end loop;
end ;
Notice the parentheses surrounding the FROM parameter of the EXTRACT function.
I managed to find a solution for my case using the below code
to_char(from_tz(to_timestamp(START_TIME, 'YYYYMMDDHH24MISS'),
substr(UTC_TIME_CODE_OFFSET,1,3)||':'||'00') at local,'yyyymmdd') as local_tstz
I was able to extract year, month and day from a not real column,at least this solution avoid me make sub-query for the above case.

PL/SQL newbie: Functions with SELECT and SYSDATE

I'm new to SQL and was hoping for some help turning this into a function:
SELECT
SYSDATE "Today's Date",
NEXT_DAY(trunc(SYSDATE, 'MONTH')-1, 'Tuesday')"First Tuesday this Month",
NEXT_DAY (LAST_DAY(SYSDATE)+1,'TUESDAY') "First Tuesday of Next Month"
FROM DUAL;
Here's my attempt at turning the above into a function (as you can see it didn't go so hot)...
CREATE OR REPLACE FUNCTION first_tuesday
RETURN DATE
AS
date1 DATE;
date2 DATE;
date3 DATE;
BEGIN
date1 := SELECT SYSDATE;
date2 := SELECT NEXT_DAY(trunc(SYSDATE, 'MONTH')-1, 'Tuesday');
date3 := SELECT NEXT_DAY (LAST_DAY(SYSDATE)+1,'TUESDAY');
RETURN 'todays date:' || date1,
'first tuesday this month:' || date2,
'first tuesday next month:' || date3;
END;
/
SELECT first_tuesday FROM DUAL;
What am I doing wrong?
You're really trying to return three values there. I'd suggest breaking it out into separate functions, but leave "today's date" just as a call to sysdate.
You don't need to issue a SELECT query to populate a variable, and in this case you can just return an expression.
CREATE OR REPLACE FUNCTION first_tuesday
RETURN DATE
AS
BEGIN
RETURN NEXT_DAY(trunc(SYSDATE, 'MONTH')-1, 'Tuesday')
END;
/
Something to look out for here is that within the execution of a SQL statement, SYSDATE always has the same value, but that's not the case in PL/SQL. So when sysdate is evaluated in PL/SQL that is called from a SQL statement, you can get a different value in each context. So if you call:
select sysdate col1,
some_function col2
from dual;
... and during it's lengthy execution some_function calculates sysdate, it could be using a slightly different (later) value than the parent SQL statement.
The way to avoid this is to pass sysdate into the function:
CREATE OR REPLACE FUNCTION first_tuesday (sysdate_in date)
RETURN DATE
AS
BEGIN
RETURN NEXT_DAY(trunc(sysdate_in, 'MONTH')-1, 'Tuesday')
END;
/
So then you might ...
Select ...
From invoices
Where invoice_date < first_tuesday(sysdate)
That's a trivial example just to demonstrate the principle, and it really only matters for functions called from SQL.
To be honest, most practitioners in your situation would probably skip the function and just:
Select ...
From invoices
Where invoice_date < NEXT_DAY(trunc(sysdate, 'MONTH')-1, 'Tuesday')
Edit: by the way, you probably want to use:
NEXT_DAY (LAST_DAY(SYSDATE),'TUESDAY')
... not ...
NEXT_DAY (LAST_DAY(SYSDATE)+1,'TUESDAY')

Oracle sql - date subtraction within a function

(moved from previous post) - sorry if this is seen as a repeat!
Hi everyone,
Just having some issues with date calculations and subtracting from a date within a function.
I am confused with the data types I should be using as I am having to convert from date to_char for example. I'm not sure whether to have the return type as varchar2 or date?
Here is the function that receives a car_id, looks in the table for that car, pulls out the cars arrive date, stores it in a date variable
does the same with the departure date.
Then converts both dates to_char, does the subtraction and returns it in a varchar2.
When I call the function it will inputting the car_id as a parameter,
then storing the returned result in a varchar variable, for a dbms output.
eg.
v_result := get_duration('0001')
here is the function:
DROP FUNCTION get_duration;
CREATE FUNCTION get_duration (p_car_id number)
RETURN varchar2 is
v_arrive date;
v_depart date
v_duration varchar2(25); --not too sure about this variable choice
begin
select arrival, departure
into v_arrive, v_depart
from car_info
where car_id = p_car_id;
v_duration := to_char(v_depart, 'dd-mon-yyyy hh24:mi:ss') - to_char(v_arrive, 'dd-mon-yyyy hh24:mi:ss')
return v_duration;
END;
/
As you can see, I am trying to put the start and end times from the table in to the variables, then minus the end date from the start date.
The function compiles, but with a warning, and when I got to call the function, the error: get_duration is invalid
Any input on this would be greatly received,
Sorry if the post is relatively big!
regards,
Darren
Trivial problems are that you're missing a ; when you define v_depart, and at the end of the line you assign the value to v_duration; and you're mixing up your variable names. (You're also inconsistent about the type of car_info.id; you've created it as a varchar when it probably ought to be a number, but that's more of a comment on your previous question).
The main problem is that you can't perform a minus on two strings, as that doesn't really mean anything. You need to do the manipulation of the original dates, and then figure out how you want to return the result to the caller.
Subtracting one date from another gives a number value, which is the number of days; partial days are fractions, so 0.25 is 6 hours. With the dates from your previous quesiton, this query:
select arrival, departure, departure - arrival as duration
from car_info
where car_id = 1;
... shows duration of 2.125, which is 2 days and 3 hours.
This isn't the best way to do this, but to show you the process of what's going on I'll use that duration number and convert it into a string in quite a long-winded way:
CREATE OR REPLACE FUNCTION get_duration (p_car_id number)
RETURN varchar2 is
v_arrive date;
v_depart date;
v_duration number;
v_days number;
v_hours number;
v_minutes number;
v_seconds number;
BEGIN
select arrival, departure, departure - arrival
into v_arrive, v_depart, v_duration
from car_info
where car_id = p_car_id;
-- Days is the whole-number part, which you can get with trunc
v_days := trunc(v_duration);
-- Hours, minutes and seconds are extracted from the remainder
v_hours := trunc(24 * (v_duration - v_days));
v_minutes := trunc(60 * (v_duration - v_days - (v_hours/24)));
v_seconds := trunc(60 * (v_duration - v_days - (v_hours/24)
- (v_minutes/(24*60))));
return v_days || ' days '
|| to_char(v_hours, '00') || ' hours '
|| to_char(v_minutes, '00') || ' minutes '
|| to_char(v_seconds, '00') || ' seconds';
END;
/
Function created.
show errors
No errors.
select get_duration(1) from dual;
GET_DURATION(1)
--------------------------------------------------------------------------------
2 days 03 hours 00 minutes 00 seconds
You can play with the number format masks etc. to get the output you want.
DATE arithmetic works on DATEs not on VARCHAR2s:
CREATE FUNCTION get_duration (p_car_id number)
RETURN varchar2 is
v_arrive date;
v_depart date
v_duration number; --<<<
begin
select arrival, departure
into v_arrive, v_depart
from car_info
where car_id = p_car_id;
v_duration := v_arrive - v_depart;
return v_duration;
END;
That gives a result in days.
You cannot subtract dates once you cast them into varchar2 (to_char function does that!). Instead use v_duration := v_stop - v_start; which will give you the result 'v_duration' in days. Then you can convert it to hours, minutes, etc..

Oracle equivalent to SQL Server/Sybase DateDiff

We are now using NHibernate to connect to different database base on where our software is installed. So I am porting many SQL Procedures to Oracle.
SQL Server has a nice function called DateDiff which takes a date part, startdate and enddate.
Date parts examples are day, week, month, year, etc. . .
What is the Oracle equivalent?
I have not found one do I have to create my own version of it?
(update by Mark Harrison) there are several nice answers that explain Oracle date arithmetic. If you need an Oracle datediff() see Einstein's answer. (I need this to keep spme SQL scripts compatible between Sybase and Oracle.) Note that this question applies equally to Sybase.
I stole most of this from an old tom article a few years ago, fixed some bugs from the article and cleaned it up. The demarcation lines for datediff are calculated differently between oracle and MSSQL so you have to be careful with some examples floating around out there that don't properly account for MSSQL/Sybase style boundaries which do not provide fractional results.
With the following you should be able to use MSSQL syntax and get the same results as MSSQL such as SELECT DATEDIFF(dd,getdate(),DATEADD(dd,5,getdate())) FROM DUAL;
I claim only that it works - not that its effecient or the best way to do it. I'm not an Oracle person :) And you might want to think twice about using my function macros to workaround needing quotes around dd,mm,hh,mi..etc.
(update by Mark Harrison) added dy function as alias for dd.
CREATE OR REPLACE FUNCTION GetDate
RETURN date IS today date;
BEGIN
RETURN(sysdate);
END;
/
CREATE OR REPLACE FUNCTION mm RETURN VARCHAR2 IS BEGIN RETURN('mm'); END;
/
CREATE OR REPLACE FUNCTION yy RETURN VARCHAR2 IS BEGIN RETURN('yyyy'); END;
/
CREATE OR REPLACE FUNCTION dd RETURN VARCHAR2 IS BEGIN RETURN('dd'); END;
/
CREATE OR REPLACE FUNCTION dy RETURN VARCHAR2 IS BEGIN RETURN('dd'); END;
/
CREATE OR REPLACE FUNCTION hh RETURN VARCHAR2 IS BEGIN RETURN('hh'); END;
/
CREATE OR REPLACE FUNCTION mi RETURN VARCHAR2 IS BEGIN RETURN('mi'); END;
/
CREATE OR REPLACE FUNCTION ss RETURN VARCHAR2 IS BEGIN RETURN('ss'); END;
/
CREATE OR REPLACE Function DateAdd(date_type IN varchar2, offset IN integer, date_in IN date )
RETURN date IS date_returned date;
BEGIN
date_returned := CASE date_type
WHEN 'mm' THEN add_months(date_in,TRUNC(offset))
WHEN 'yyyy' THEN add_months(date_in,TRUNC(offset) * 12)
WHEN 'dd' THEN date_in + TRUNC(offset)
WHEN 'hh' THEN date_in + (TRUNC(offset) / 24)
WHEN 'mi' THEN date_in + (TRUNC(offset) /24/60)
WHEN 'ss' THEN date_in + (TRUNC(offset) /24/60/60)
END;
RETURN(date_returned);
END;
/
CREATE OR REPLACE Function DateDiff( return_type IN varchar2, date_1 IN date, date_2 IN date)
RETURN integer IS number_return integer;
BEGIN
number_return := CASE return_type
WHEN 'mm' THEN ROUND(MONTHS_BETWEEN(TRUNC(date_2,'MM'),TRUNC(date_1, 'MM')))
WHEN 'yyyy' THEN ROUND(MONTHS_BETWEEN(TRUNC(date_2,'YYYY'), TRUNC(date_1, 'YYYY')))/12
WHEN 'dd' THEN ROUND((TRUNC(date_2,'DD') - TRUNC(date_1, 'DD')))
WHEN 'hh' THEN (TRUNC(date_2,'HH') - TRUNC(date_1,'HH')) * 24
WHEN 'mi' THEN (TRUNC(date_2,'MI') - TRUNC(date_1,'MI')) * 24 * 60
WHEN 'ss' THEN (date_2 - date_1) * 24 * 60 * 60
END;
RETURN(number_return);
END;
/
JohnLavoie - you don't need that. DATE in Oracle is actually a date and time data type. The only difference between DATE and TIMESTAMP is that DATE resolves down to the second but TIMESTAMP resolves down to the micro second. Therefore the Ask Tom article is perfectly valid for TIMESTAMP columns as well.
Tom's article is very old. It only discusses the DATE type. If you use TIMESTAMP types then date arithmetic is built into PL/SQL.
http://www.akadia.com/services/ora_date_time.html
DECLARE
ts_a timestamp;
ts_b timestamp;
diff interval day to second;
BEGIN
ts_a := systimestamp;
ts_b := systimestamp-1/24;
diff := ts_a - ts_b;
dbms_output.put_line(diff);
END;
+00 01:00:00.462000
or
DECLARE
ts_b timestamp;
ts_a timestamp;
date_part interval day to second;
BEGIN
ts_a := systimestamp;
date_part := to_dsinterval('0 01:23:45.678');
ts_b := ts_a + date_part;
dbms_output.put_line(ts_b);
END;
04-SEP-08 05.00.38.108000 PM
YOU Could write a function in oracle for this
function datediff( p_what in varchar2, p_d1 in date, p_d2 in date) return number as l_result number;
BEGIN
select (p_d2-p_d1) *
decode( upper(p_what), 'SS', 24*60*60, 'MI', 24*60, 'HH', 24, NULL )
into l_result from dual;
return l_result;
END;
and use it like :
DATEDIFF('YYYY-MM-DD', SYSTIMESTAMP, SYSTIMESTAMP)