How do I join a string and an int in PostgreSQL? - sql

I have a procedure with an int parameter.
CREATE OR REPLACE PROCEDURE update_retention_policy(id int, days int)
language plpgsql
AS
$$
BEGIN
PERFORM add_retention_policy(('schema_' + id + '.my_hypertable'), days * INTERVAL '1 day', true);
END
$$;
The syntax for the add_retention_policy function is add_retention_policy('hypertable', INTERVAL 'x days', true). I want to prefix the hypertable with the schema which is always 'schema_' and then followed by the id parameter, how do I do that?

You just need to rewrite the INTERVAL part in your function call as days * INTERVAL '1 day'.
Instead of concatenating strings, you multiply the '1 day' interval by the days param.
EDIT: for the id part, you can just use the || operator, which is the string concatenation operator in Postgres, instead of +. You shouldn't even need to explicitly cast id to character varying

Related

Add one day to Now() and return as default value in SQL query

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.

Read & evaluate cell as keyword in PostgreSQL

I have a table called providers where the column notice_period takes values such as interval '1 month' - interval '2 days'. If I do the following:
select to_date('11.11.2020', 'dd-mm-yyyyy') + notice_period from providers
I get the following error:
42883] ERROR: operator does not exist: ` text
How can I avoid this error and use the value of notice_period to directly calculate a valid date?
Cheers
Postgres readily converts intervals to strings. I would recommend removing the interval key word and just using ::interval:
select date_trunc('year', now()) + notice_period::interval
from (values ('1 day'), ( '3 month 1 day')) v(notice_period)
If you insist on the interval in the string, then remove it:
+ replace(notice_period, 'interval ', '')::interval
or:
+ substr(notice_period, 10)::interval
Another option is to use dynamic SQL in a function:
CREATE FUNCTION add_period(timestamp with time zone) RETURNS timestamp with time zone
LANGUAGE plpgsql STRICT AS
$$DECLARE
result timestamp with time zone;
BEGIN
EXECUTE format(
'SELECT TIMESTAMP WITH TIME ZONE %L + %s',
$1,
(SELECT notice_period FROM providers)
) INTO result;
RETURN result;
END;$$;
The problem with that is that the whole thing is vulnerable to SQL injection by bad values in the table providers, so use that only if you can trust the source.
This problem cannot be avoided with SQL strings supplied from outside.

How to pass the number of days to a Postgres function?

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 ...

Oracle. Select and function

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.

Postgresql current datetime for past year

Is there a function on postgresql that will let me get the current datetime for the past year or x number of past years?
I know i can do this select now() - interval '1 year'; but in a function how can i put the number of years in a variable
x := '2 year'
Is it possible to do this select now() - interval x;
I tried but it give me error
If you want to use variable you can do this:
CREATE OR REPLACE FUNCTION func(input integer)
RETURNS TIMESTAMP WITHOUT TIME ZONE AS
$BODY$
declare
result TIMESTAMP WITHOUT TIME ZONE;
begin
select now() - (input || ' years')::interval into result;
return result;
end;
$BODY$
LANGUAGE plpgsql VOLATILE
For the date, you would use:
select current_date - interval '1 year'
For the date/time:
select now() - interval '1 year'