How to pass a parameter into a date function
Asked Answered
S

2

0

I am trying to create a simple function and I cannot seem to pass in a parameter into the date function.
Here is the function:

CREATE OR REPLACE FUNCTION test(source int,days text)
RETURNS integer AS $totals$
declare
    totals integer;
BEGIN

   select 
           count(id) into totals
     from  ad
    where
       createdate::date = date(current_date - interval '$2' day) and
       source = $1;

   RETURN totals;

END;
$totals$ LANGUAGE plpgsql;
Scatter answered 10/6, 2015 at 14:4 Comment(2)
Your version of Postgres, the table definition and the error message you get would be essential details.Innards
Yes the answers here worked. I was testing against an older version of the function... Thank you for the in depth answer.Scatter
I
0

@IMSoP already shed light upon your syntax error. However, this can be simpler, faster and cleaner in multiple ways.

CREATE OR REPLACE FUNCTION test(_source int, _days int)
  RETURNS integer AS
$func$
SELECT count(*)::int
FROM   ad a
WHERE  a.source = $1
AND    a.createdate::date = current_date - $2
$func$  LANGUAGE sql STABLE;
  • First of all, to subtract days from a date, you can can just subtract an integer number. Accordingly I use an integer parameter here.

  • You don't need plpgsql for a simple function like this. Use an SQL function instead - which can be "inlined" in the context of a bigger query, and thus optimized better in certain cases.

  • The function can be STABLE:

  • You had a naming conflict built into your function. source is obviously also a column name. Try to avoid that. A common practice is to prepend variables and parameters with an underscore (which has no special meaning otherwise). You can also table-qualify column names and/or prepend parameter names with the function name (or use positional parameters) to be unambiguous. I did both here.

  • Assuming id is your PK column and thus defined NOT NULL, count(*) does the same as count(id), a bit shorter and cheaper. I cast to integer, because count() will return a bigint.

However, going out on a limb here, I suspect your inaccurately named column createdate is not actually a date but a timestamp (essential table definition is missing in the question). In that case it's much more efficient to phrase the query differently:

CREATE OR REPLACE FUNCTION test(_source int, _days int)
  RETURNS integer AS
$func$
SELECT count(*)::int
FROM   ad a
WHERE  a.source = $1
AND    a.createdate >= now() - interval '1 day' * $2
AND    a.createdate <  now() - interval '1 day' * ($2 - 1)
$func$ LANGUAGE sql STABLE;
  • This expression is sargable and thus more efficient. It can also use a plain index on (createdate), or better on (source, createdate) - important for big tables.

  • Also demonstrating an alternative way to subtract days. You can multiply the interval '1 day'. Related:

Innards answered 10/6, 2015 at 17:30 Comment(0)
G
0

This is invalid syntax: interval '$2' day. The variable isn't just swapped into place before the SQL is run, you need to pass the right kind of value to the function.

You probably meant to concatenate the string 'day' onto the end of the string (text) variable $2, also known as days (not the literal string '$2'). So $2 || 'day' or days || 'day'.

Since this is then not a single literal, you need an explicit cast, not just a type label, so something like CAST($2 || 'day' AS interval) or (days || 'day')::interval.

Gaff answered 10/6, 2015 at 14:17 Comment(3)
Thanks for the reply. I have tried this: date = date(current_date - (days || 'day')::interval) nut it does not work. Is this as you expected?Scatter
@Scatter Please clarify "does not work". Do you get an error message - if so, what message? Does it do the "wrong" thing - if so, what does it do and what do you want it to do instead?Gaff
I'm very sorry. I was testing against a different function. Your solution works!! Thank you very very much.Scatter
I
0

@IMSoP already shed light upon your syntax error. However, this can be simpler, faster and cleaner in multiple ways.

CREATE OR REPLACE FUNCTION test(_source int, _days int)
  RETURNS integer AS
$func$
SELECT count(*)::int
FROM   ad a
WHERE  a.source = $1
AND    a.createdate::date = current_date - $2
$func$  LANGUAGE sql STABLE;
  • First of all, to subtract days from a date, you can can just subtract an integer number. Accordingly I use an integer parameter here.

  • You don't need plpgsql for a simple function like this. Use an SQL function instead - which can be "inlined" in the context of a bigger query, and thus optimized better in certain cases.

  • The function can be STABLE:

  • You had a naming conflict built into your function. source is obviously also a column name. Try to avoid that. A common practice is to prepend variables and parameters with an underscore (which has no special meaning otherwise). You can also table-qualify column names and/or prepend parameter names with the function name (or use positional parameters) to be unambiguous. I did both here.

  • Assuming id is your PK column and thus defined NOT NULL, count(*) does the same as count(id), a bit shorter and cheaper. I cast to integer, because count() will return a bigint.

However, going out on a limb here, I suspect your inaccurately named column createdate is not actually a date but a timestamp (essential table definition is missing in the question). In that case it's much more efficient to phrase the query differently:

CREATE OR REPLACE FUNCTION test(_source int, _days int)
  RETURNS integer AS
$func$
SELECT count(*)::int
FROM   ad a
WHERE  a.source = $1
AND    a.createdate >= now() - interval '1 day' * $2
AND    a.createdate <  now() - interval '1 day' * ($2 - 1)
$func$ LANGUAGE sql STABLE;
  • This expression is sargable and thus more efficient. It can also use a plain index on (createdate), or better on (source, createdate) - important for big tables.

  • Also demonstrating an alternative way to subtract days. You can multiply the interval '1 day'. Related:

Innards answered 10/6, 2015 at 17:30 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.