How to pass a parameter into a date function

我是研究僧i 提交于 2019-12-12 01:25:02

问题


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;

回答1:


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

    • How do I determine the last day of the previous month using PostgreSQL?
  • 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:

    • How do IMMUTABLE, STABLE and VOLATILE keywords effect behaviour of function?
  • 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:

    • Query using two column values to create range



回答2:


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.



来源:https://stackoverflow.com/questions/30758691/how-to-pass-a-parameter-into-a-date-function

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!