Is there a function in PostgreSQL that is the same as NUMTODSINTERVAL(n, interval unit) in Oracle?
Just multiply your variable with the desired interval:
interval '1' day * n
Since Postgres 9.4 you can also use the function make_interval()
make_interval(days => n)
If you want a functionality similar to this function (i.e. the unit is variable -- not constant): a simple concatenation & a cast is enough in PostgreSQL:
select cast(num || unit as interval)
SQLFiddle
You can read more about interval's input formats here.
'1 day'::interval
do something like this.
Related
I am attempting to add a day to NOW() and return as the values for a column.
This works
SELECT NOW() as date
But this gives an error
SELECT DATE_ADD( NOW(), INTERVAL 1 DAY) as date
Is there a way to achieve this in a postgres query?
Thanks
I don't think there's a date_add() function in PostgreSQL:
ERROR: function date_add(timestamp with time zone, interval) does not
exist
LINE 1: select date_add(now(), interval '1 day');
^
HINT: No function matches the given name and argument types. You
might need to add explicit type casts.
but you can use a regular + operator to add an interval to timestamptz that's returned by now(). Demo:
select now() + '1 day'::interval;
You can define that function for convenience:
create function date_add(arg1 timestamptz, arg2 interval)
returns timestamptz language sql as $$
select arg1+arg2
$$;
select date_add(now(), interval '1 day') as date;
-- date
---------------------------------
-- 2022-11-29 12:28:12.393508+00
But I don't think it's really more convenient than the operator. You'd also have to overload it to make sure how it deals with different types - you can see in the demo how by default PostgreSQL will try to guess and cast automatically.
I am trying to convert the DAY() TO SECOND function in teradata to GCP sql.
Can someone help me convert this?
AVERAGE(((run_end_dttm - run_start_dttm )DAY(4) TO SECOND )) AS elapsed_time,
As was mentioned Bigquery doesn't support INTERVAL data type what actually Teradata DAY(4) TO SECOND function returns.
If your aim stands to get an interval difference between two timestamps in the compliant to Teradata output format i.e. day hour:minute:second.millisecond you might consider to write your own Bigquery UDF function which will afford this transformation.
Below I'm sharing Bigquery function prototype for this kind of conversion, leveraging some of the Timestamp and Time built-in functions:
CREATE TEMP FUNCTION
time_conv(t1 timestamp,
t2 timestamp) AS ((
SELECT
FORMAT( '%d %d:%d:%d.%d', ABS(day), EXTRACT(hour
FROM
time(second)), EXTRACT(minute
FROM
time(second)), EXTRACT(second
FROM
time(second)), EXTRACT(millisecond
FROM
time(second)) ) AS output
FROM
UNNEST([STRUCT( TIMESTAMP_DIFF(t1,t2, day) AS day,
TIMESTAMP_SECONDS(TIMESTAMP_DIFF(t1,t2, second)) AS second )]) ));
WITH
`example` AS (
SELECT
TIMESTAMP("2021-10-19 21:45:21") AS t1,
TIMESTAMP("2021-10-15 18:17:56") AS t2 )
SELECT
time_conv(t1,
t2)
FROM
example
You can also tweak Bigquery FORMAT() block getting the desirable output.
Argument days in function getAvgByDay() doesn't work, I guess because it is inside quotes:
CREATE OR REPLACE FUNCTION getAvgByDay(days int)
RETURNS TABLE ( average text,
date timestamp with time zone
) AS
$func$
BEGIN
RETURN QUERY
SELECT to_char( AVG(measure), '99999D22') AS average, ( now() - interval '$1 day') AS date
FROM (
SELECT mes.date, mes.measure
FROM measures mes
WHERE mes.date < ( now() - interval '$1 day')
) AS mydata;
END
$func$
LANGUAGE plpgsql;
Assuming the column measures.date is actually data type timestamptz and not a date:
CREATE OR REPLACE FUNCTION get_avg_by_day(_days int)
RETURNS TABLE (average text, ts timestamptz) AS -- not using "date" for a timestamp
$func$
SELECT to_char(avg(measure), '99999D22') -- AS average
, now() - interval '1 day' * $1 -- AS ts
FROM measures m
WHERE m.date < now() - interval '1 day' * $1
$func$ LANGUAGE sql;
No need for PLpgSQL, can be a simper SQL function.
Difference between language sql and language plpgsql in PostgreSQL functions
No need for a subquery. Only adds complexity and cost for no gain.
No need for column aliases in the outer query level. Those are not used, as visible column names are defined in the RETURNS clause.
No need for extra parentheses. Operator precedence works as desired anyway. (No harm in this case, either.)
Don't use CaMeL case identifier in Postgres if you can avoid it.
Are PostgreSQL column names case-sensitive?
Don't call a timestamptz column "date". That's misleading. Using "ts" instead.
Most importantly: You suspected as much, and "sticky bit" already explained: no interpolation inside strings. But just multiply the time unit with your integer input to subtract the given number of days:
interval '1 day' * $1
That's faster and cleaner than string concatenation.
There's no interpolation in strings. But you can concatenate strings and cast them to an interval. Try:
... concat(days, ' day')::interval ...
Or you could use format(), that's probably a little closer to what you originally had:
... format('%s day', days)::interval ...
I am trying to build a simple TIMESTAMP_AGO SQL UDF. The function is a simple wrapper around CURRENT_TIMESTAMP and TIMESTAMP_SUB.
I want to call it, with signature:
SELECT TIMESTAMP_AGO(24, 'HOUR');
or, even:
SELECT TIMESTAMP_AGO(24 HOUR);
But BigQuery does not seem to like the date_part of INTERVAL as a variable, so it fails. I've tried a separation of arguments:
CREATE TEMP FUNCTION TIMESTAMP_AGO(_interval INT64, _date_part STRING) AS ((
SELECT TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL _interval _date_part)
));
and, trying to pass an INTERVAL as well :
CREATE TEMP FUNCTION TIMESTAMP_AGO(_interval INTERVAL) AS ((
SELECT TIMESTAMP_SUB(CURRENT_TIMESTAMP(), _interval)
));
Can INTERVAL's be passed around like this?
Or, is it possible to pass a dynamic date_part?
Failing these, would it be possible to use an External UDF (JS)?
Below is for BigQuery Standard SQL
TIMESTAMP_SUB supports the following values for date_part:
MICROSECOND
MILLISECOND
SECOND
MINUTE
HOUR
So, you just simply need to check your passed _date_part and use respective "version" as in below example
#standardSQL
CREATE TEMP FUNCTION TIMESTAMP_AGO(_interval INT64, _date_part STRING) AS (
CASE _date_part
WHEN 'MICROSECOND' THEN TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL _interval MICROSECOND)
WHEN 'MILLISECOND' THEN TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL _interval MILLISECOND)
WHEN 'SECOND' THEN TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL _interval SECOND)
WHEN 'MINUTE' THEN TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL _interval MINUTE)
WHEN 'HOUR' THEN TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL _interval HOUR)
END
);
So, now below will work
SELECT TIMESTAMP_AGO(24, 'HOUR')
You can obviously add UPPER() to CASE _date_part if you expect case-insensitive input, etc.
I have function like this:
CREATE OR REPLACE FUNCTION IntervalToSec(Run_Duration interval day to second) RETURN NUMBER IS
vSeconds NUMBER ;
BEGIN
SELECT EXTRACT( DAY FROM Run_Duration ) * 86400
+ EXTRACT( HOUR FROM Run_Duration ) * 3600
+ EXTRACT( MINUTE FROM Run_Duration ) * 60
+ EXTRACT( SECOND FROM Run_Duration )
INTO
vSeconds
FROM DUAL ;
RETURN vSeconds ;
END;
That converts interval data to total seconds number
Then i have select query:
select RUN_DURATION from SYS.USER_SCHEDULER_JOB_RUN_DETAILS WHERE JOB_NAME = 'WASTE_MESSAGES_JOB' and LOG_DATE > (systimestamp - INTERVAL '0 00:10:00.0'DAY TO SECOND(1) )
order by LOG_DATE desc;
Output like:+00 00:00:01.000000
Question is how can i pipe query result into function?
Thank you!
A user defined function doesn't behave any differently to built-in functions, so you call it as you would any other function - to_char, to_date, trunc, round etc.
You can write user-defined functions in PL/SQL, Java, or C to provide functionality that is not available in SQL or SQL built-in functions. User-defined functions can appear in a SQL statement wherever an expression can occur.
For example, user-defined functions can be used in the following:
The select list of a SELECT statement
...
So just call the function as part of the query:
select IntervalToSec(RUN_DURATION)
from SYS.USER_SCHEDULER_JOB_RUN_DETAILS
WHERE JOB_NAME = 'WASTE_MESSAGES_JOB'
and LOG_DATE > (systimestamp - INTERVAL '0 00:10:00.0'DAY TO SECOND(1) )
order by LOG_DATE desc;
As long as the queried column is the same data type the function expects (or can be implicitly converted to that type) it can be passed as the function's argument.
https://docs.oracle.com/cd/B13789_01/appdev.101/b10807/13_elems045.htm
I believe a select Into statement will work for this? If you just want the query results to go into the function statement. Parameters for the into say it will take a function_name. Of course I could be way off as I've never used it for getting information into functions.