问题
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 aninteger
number. Accordingly I use aninteger
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 definedNOT NULL
,count(*)
does the same ascount(id)
, a bit shorter and cheaper. I cast tointeger
, because count() will return abigint
.
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