0

有问题的代码:

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;

我需要找到 id 元素的树深度searchid,我编写了一个与上面略有不同的函数,它mid NOTNULL用作 while 循环的条件并且它可以工作。

但是,当我尝试EXISTS直接在 WHILE 条件下使用上面发布的代码时,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 上运行。

4

3 回答 3

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
于 2012-12-05T21:18:37.917 回答
1

关键错误是您不能在构造SELECT INTO内部分配变量EXISTSSELECT结构内的EXISTS项目被忽略。

我重写了函数以简化并使其更安全:

CREATE OR REPLACE FUNCTION foo(_searchid int, OUT _level int)
  RETURNS int
  LANGUAGE plpgsql STABLE 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$;

称呼:

SELECT foo(1);

要点

  • 防止循环无限是您的责任。

  • 参数的_前缀是为了避免与使用表的潜在列发生命名冲突。

  • 我使用在某些 SQL 语句(如)找到一行之后(并且仅在之后)FOUND设置的特殊变量。TRUESELECT INTO

  • EXIT当没有找到行时,使用该命令退出循环。

  • _level内增量SELECT。(或者在循环体中,它只是一个小小的简化。)

  • 从 PostgreSQL 9.1开始,您可以分配给IN参数,所以我(ab)使用_searchid并且不需要 DECLARE 任何其他变量。如果您稍后在函数中需要原始参数值,请不要这样做。

  • 该函数不应该被声明IMMUTABLE,因为它访问一个表。我做到了STABLE。您可以使该函数IMMUTABLE“作弊”并能够在索引创建中使用它(例如)-但是如果在基础表更改后此类索引中断,则由您自己决定。

递归 CTE

使用现代 PostgreSQL,您还可以使用递归 CTE来完成这项工作。这就是@a_horse 在他的评论中暗示的- 哦,以及他现在作为answer发布的内容。
另一个例子(SO 上的许多例子)在这里

于 2012-12-05T20:47:40.897 回答
0

也许您不想编写自己的函数,而是将 postgres 扩展与创建用于呈现存储在表中的分层数据的函数一起使用?它被称为 connectby,它是 tablefunc 扩展的一部分。如何使用您可以在此处找到的功能。

要安装扩展:

 CREATE EXTENSION tablefunc;

您可以选择许多可能性:开始行的键值,下降到的最大深度,或零表示无限深度,或字符串以在分支输出中分隔键。

于 2013-01-25T12:07:39.880 回答