PostgreSQL EXISTS 在用户定义函数中始终返回 true

问题描述 投票:0回答:2

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

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 位

sql postgresql naming-conventions function-parameter
2个回答
3
投票

就像 @wildplasser 暗示的那样,你的函数参数

eid
与表列同名,这是 never 一个好主意。在这种情况下,它默默地破坏了你的功能。

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

说明书:

如果参数名与当前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$;

db<>小提琴这里

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


0
投票

也许这可能对任何人都有帮助,它与这个问题的关键字有关,但我也发现了该查询

select 1 from custom_function_returning_non_set();

始终返回 1。因此导致

exists
子句总是返回
true

当函数更改为

returns setof
并且实际返回空集时,
select 1
返回
null
exists
返回
false

因此,一开始使用

exists
且函数不返回
setof
可能是一个坏主意。 (至少就我而言......)

© www.soinside.com 2019 - 2024. All rights reserved.