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 ループの条件として使用され、機能します。

ただし、上記のコードのようにEXISTSWHILE 条件で直接使用しようとすると、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 を使用している場合は、次の 1 つのステートメントでより効率的に実行できます。

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し、宣言する必要はありません。関数の後半で元のパラメーター値が必要な場合は、これを行わないでください。

  • IMMUTABLEこの関数はテーブルにアクセスするため、宣言しないでください。代わりに作りましSTABLEた。関数を「チート」して、インデックスの作成に使用できるようにすることができます (たとえば)IMMUTABLE

再帰的 CTE

最新の PostgreSQLでは、ジョブに再帰 CTEを使用することもできます。それは@a_horseが彼のコメントでほのめかしたものです-ああ、そして彼が今答えとして投稿したもの.
別の例 (SO に関する多くの例) here .

于 2012-12-05T20:47:40.897 に答える
0

独自の関数を作成する代わりに、テーブルに格納された階層データを表示するために作成された関数で postgres 拡張機能を使用したい場合がありますか? これは connectby と呼ばれ、tablefunc 拡張機能の一部です。ここで見つけることができる機能の使用方法

拡張機能をインストールするには:

 CREATE EXTENSION tablefunc;

選択できるように、多くの可能性があります: 開始する行のキー値、下降する最大の深さ、または無制限の深さのゼロ、またはブランチ出力でキーを区切る文字列。

于 2013-01-25T12:07:39.880 に答える