我编写了一个简单的用户定义函数来检查是否存在匹配某些条件的行:
CREATE OR REPLACE FUNCTION is_instructor_specialized_in(eid INT, course_area VARCHAR(50))
RETURNS BOOLEAN AS $$
SELECT EXISTS(SELECT 1 FROM Specializes s WHERE s.eid = eid AND s.name = course_area);
$$ LANGUAGE sql;
我用以下查询测试了它:
SELECT is_instructor_specialized_in(2, 'Artificial Intelligence') as function_output,
EXISTS(SELECT 1 FROM Specializes s WHERE s.eid = 2 AND s.name = 'Artificial Intelligence') as ground_truth;
并且该函数在应该计算为
true
时给出了错误的 false
值(Specializes
表中没有这样的行):
图片
事实上,它总是给出
true
的值。我超级困惑。发生这种情况有什么原因吗?
版本:x86_64-apple-darwin19.6.0 上的 PostgreSQL 13.2,由 Apple clang 版本 11.0.3 (clang-1103.0.32.62) 编译,64 位
就像 @wildplasser 暗示的那样,你的函数参数
eid
与表列同名,这是 never 一个好主意。在这种情况下,它默默地破坏了你的功能。
eid
中的不合格的WHERE s.eid = eid
解析为表列,而不是函数参数,就像您所期望的那样。因此,对于任何非空输入,该谓词的计算结果为 true
。偷偷摸摸的错误。
如果参数名与当前SQL中任意列名相同 函数内的命令,列名称优先。到 覆盖它,用函数名称限定参数名称 本身,那就是
。 (如果这会 与限定的列名冲突,再次以列名胜出。你 可以通过为表选择不同的别名来避免歧义 在 SQL 命令中。)function_name.argument_name
我的粗体强调。
使用函数名称进行限定是最后手段的尴尬措施。从明确的参数名称开始避免这个问题。一种约定是在参数前添加下划线 (
_
) - 并且永远不要对表列执行相同的操作:
CREATE OR REPLACE FUNCTION func_proper(_eid int, _course_area text)
RETURNS boolean
LANGUAGE sql STABLE PARALLEL SAFE AS
$func$
SELECT EXISTS(SELECT FROM specializes s WHERE s.eid = _eid AND s.name = _course_area);
$func$;
或者对像您这样的简单情况使用位置
$n
参数引用。您仍然可以为文档和命名函数调用使用参数名称:
CREATE OR REPLACE FUNCTION func_proper(_eid int, _course_area text)
RETURNS boolean
LANGUAGE sql STABLE PARALLEL SAFE AS
$func$
SELECT EXISTS(SELECT FROM specializes s WHERE s.eid = $1 AND s.name = $2);
$func$;
db<>小提琴这里
顺便说一句,PL/pgSQL 函数中相同命名冲突的默认行为是引发异常。参见:
也许这可能对任何人都有帮助,它与这个问题的关键字有关,但我也发现了该查询
select 1 from custom_function_returning_non_set();
始终返回 1。因此导致
exists
子句总是返回 true
。
当函数更改为
returns setof
并且实际返回空集时,select 1
返回 null
,exists
返回 false
。
因此,一开始使用
exists
且函数不返回 setof
可能是一个坏主意。 (至少就我而言......)