用户定义函数中的 PostgreSQL EXISTS 总是返回 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 表中没有这样的行): image

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

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

解决方法

就像@wildplasser 暗示的那样,您的函数参数 eid 与表列同名,这 绝不是一个好主意。在这种情况下,它默默地破坏了您的功能。

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

The manual:

如果参数名与当前 SQL 中的任何列名相同 函数内的命令,列名将优先。到 覆盖它,用函数名限定参数名 本身,即 function_name.argument_name。 (如果这会 与限定的列名冲突,列名再次获胜。你 可以通过为表选择不同的别名来避免歧义 在 SQL 命令中。)

粗体强调我的。

使用函数名进行限定是一种尴尬的最后手段。避免以明确的参数名称开头的问题。一种约定是用下划线 (_) 为参数添加前缀 - 并且永远不要对表列执行相同的操作:

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$;

dbfiddle here

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