用户定义函数中的 PostgreSQL EXISTS 总是返回 true

PostgreSQL EXISTS in user-defined function always returning true

我编写了一个简单的用户定义函数来检查是否存在符合某些条件的行:

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 table 中没有这样的行): image

其实总是给出true的值。我超级困惑。发生这种情况有什么原因吗?

版本:x86_64-apple-darwin19.6.0 上的 PostgreSQL 13.2,由 Apple clang 版本 11.0.3 (clang-1103.0.32.62) 编译,64 位

就像@wildplasser 暗示的那样,您的函数参数 eid 与 table 列同名,即 never 好主意。在这种情况下,它默默地破坏了你的功能。

WHERE s.eid = eid 中的非限定 eid 解析为 table 列,而不是函数参数,正如您所期望的那样。因此,对于任何非空输入,此谓词的计算结果为 true。偷偷摸摸的错误。

The manual:

If the argument name is the same as any column name in the current SQL command within the function, the column name will take precedence. To override this, qualify the argument name with the name of the function itself, that is function_name.argument_name. (If this would conflict with a qualified column name, again the column name wins. You can avoid the ambiguity by choosing a different alias for the table within the SQL command.)

大胆强调我的。

用函数名限定是不得已的尴尬措施。避免以明确的参数名称开头的问题。一种约定是在参数前加上下划线 (_) - 永远不要对 table 列做同样的事情:

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 =  AND s.name = );
$func$;

db<>fiddle here

PL/pgSQL 函数中相同命名冲突的默认行为是引发异常,顺便说一下。参见: