有问题的代码:
CREATE OR REPLACE FUNCTION foo(searchid INTEGER)
RETURNS INTEGER AS
$$
DECLARE
level INTEGER := 0;
mid INTEGER := searchid;
BEGIN
WHILE EXISTS(SELECT id INTO mid FROM tbl1 WHERE parent_id=mid) LOOP
level := level + 1;
END LOOP;
RETURN level;
END;
$$
LANGUAGE 'plpgsql' IMMUTABLE;
我需要找到标识为searchid
的元素的树深度,我写了一个与上面稍微不同的函数,它使用mid NOTNULL
作为while循环的条件并且它可以工作。 / p>
但是,当我尝试在WHILE条件中直接使用EXISTS
时,如上面发布的代码,postgresql说:
SQL error:
ERROR: syntax error at or near "$1"
LINE 1: SELECT EXISTS(SELECT id INTO $1 FROM tbl1 WHERE ...
因此它对我的代码进行了一些奇怪的转换,使其语法错误。
如何解决?
它在postgresql 8.3.17上运行。
答案 0 :(得分:2)
仅供记录:
如果您使用的是Postgres的最新版本,则可以使用单个语句更有效地执行此操作:
with recursive tree as (
select id, parent, 1 as level
from tbl1
where id = 1
union all
select c.id, c.parent, p.level + 1
from tbl1 c
join tree p on c.parent = p.id
)
select max(level)
from tree
答案 1 :(得分:1)
关键错误是您无法在SELECT INTO
构造中分配带EXISTS
的变量。 <{1}}构造中的SELECT
项将被忽略。
我重写了这个函数,通常简化并使其更安全:
EXISTS
呼叫:
CREATE OR REPLACE FUNCTION foo(_searchid int, OUT _level int)
RETURNS int AS
$func$
BEGIN
_level := 0;
LOOP
SELECT INTO _level, _searchid
_level + 1, t.id FROM tbl1 t WHERE t.parent_id = _searchid;
EXIT WHEN NOT FOUND;
END LOOP;
END
$func$
LANGUAGE plpgsql STABLE;
你有责任防止循环无限。
参数的SELECT foo(1);
前缀是为了避免与使用过的表的潜在列发生命名冲突。
我在某些SQL语句(如_
)找到一行之后(仅在此之后)使用设置为TRUE
的特殊变量FOUND
。
使用EXIT
命令在没有找到行时退出循环。
在SELECT INTO
内增加_level
。 (或者在循环体中,它只是一个很小的简化。)
由于PostgreSQL 9.1 ,您可以分配SELECT
参数,因此I(ab)使用IN
并且不需要DECLARE任何其他变量。如果您稍后需要函数中的原始参数值,请不要这样做。
该函数不应声明_searchid
,因为它访问表。我改为IMMUTABLE
。你可以使函数STABLE
“欺骗”并能够在索引创建中使用它(例如) - 但如果在底层更改后这样的索引中断,那么它就在你身边表
使用现代PostgreSQL,您还可以使用recursive CTE来完成工作。这可能是@a_horse在his comment中暗示的 - 哦,以及他现在发布的answer。
一个例子(很多关于SO)here。
答案 2 :(得分:0)
也许你不想编写自己的函数,而是想使用postgres扩展来创建用于呈现存储在表中的分层数据的函数? 它叫做connectby,它是tablefunc扩展的一部分。如何使用您可以找到的功能here.
安装扩展程序:
CREATE EXTENSION tablefunc;
您可以选择许多可能性:要开始的行的键值,要下降的最大深度,无限深度的零值,或者用分支输出分隔键的字符串。