6

selectPostgresの関数をビューとして使用するクエリに変換しようとしています。その理由は、関数のようにパラメーターを使用するのではなく、句を使用したselectクエリを介してクライアントからアクセスしたいためです。whereこのテーブルはツリー(および隣接リスト)を表し、次のように定義されています。

CREATE TABLE tree (
  id serial primary key,
  parent_id int references tree(id)
);

INSERT INTO tree (id, parent_id) VALUES
  (1,null)
, (2,1), (3,2), (4,3), (5,3)
, (6,5), (7,6), (8,4), (9,8)
, (10,9), (11,9), (12,7)
, (13,12), (14,12), (15,11)
, (16,15), (17,16), (18,14)
, (19,13), (20,19), (21,20);

SELECT setval ('tree_id_seq', 21); -- reset sequence

-- This produces a tree like:
--                                                   +-- <10>
--                                                  /
--                         +-- <4> -- <8> --- <9> -+- <11> --- <15> --- <16> --- <17>
--                        /                                        
--  <1> --- <2> --- <3> -+                                       
--                        \                                  
--                         +-- <5> --- <6> --- <7> --- <12> -+- <14> --- <18>
--                                                            \               
--                                                             \            
--                                                              \                
--                                                               \                   
--                                                                +-- <13> --- <19> --- <20> --- <21>
--

ツリー内の任意のノードからルートへのパスを順番に取得するには、次の関数を使用します。

create or replace function _tree(rev int)
  returns table(id int, parent_id int, depth int) as $$
declare
  sql text;
begin
  sql = 'WITH RECURSIVE tree_list(id, parent_id, depth) AS (
          SELECT id, parent_id, 1 FROM tree  WHERE id = ' || rev || 
          'UNION 
           SELECT p.id, p.parent_id, r.depth + 1
           FROM tree p, tree_list r
           WHERE p.id = r.parent_id
         )
         SELECT id, parent_id, depth FROM tree_list order by id;';
  return query execute sql;
end;
$$ language plpgsql;

クエリは次のようになりますselect * from _tree(15)。問題は、この関数をビューに変換して、を呼び出すことができるようにするにはどうすればよいかということselect * from tree where id <= 15です。また、ビューは関数と同じ速度で実行されますか(つまり、クエリの実行にwhere句が考慮されますか)?

4

2 に答える 2

4

よりシンプルな機能

まず第一に、あなたはあなたの機能をかなり単純化することができます。この単純なSQL関数は同じことを行います。

CREATE OR REPLACE FUNCTION f_tree(_rev int)
 RETURNS TABLE(id int, parent_id int, depth int) AS
$func$
   WITH RECURSIVE tree_list AS (
      SELECT t.id, t.parent_id, 1 -- AS depth
      FROM   tree t
      WHERE  t.id = $1

      UNION ALL  -- no point using UNION
      SELECT t.id, t.parent_id, r.depth + 1
      FROM   tree_list r
      JOIN   tree t ON t.id = r.parent_id
      )
   SELECT t.id, t.parent_id, t.depth
   FROM   tree_list t
   ORDER  BY t.id;
$func$ LANGUAGE sql;

電話:

select * from f_tree(15);
  • plpgsqlを使用できますが、PostgreSQL9.2より前のバージョンでクエリプランをキャッシュするのに少し役立つ場合があります。しかし、動的SQLを必要とせずに使用することで、理論上の唯一のメリットを無効にしました。これはまったく意味がありません。プレーンSQLに単純化します。

  • 設計上重複することはありませんので、UNION ALL代わりに使用してください。UNION

SQLだけ

明らかに、これをプレーンSQLに置き換えることができます。

WITH RECURSIVE tree_list AS (
   SELECT t.id, t.parent_id, 1 AS depth
   FROM   tree t
   WHERE  t.id = 15  -- enter parameter here

   UNION ALL
   SELECT t.id, t.parent_id, r.depth + 1
   FROM   tree_list r
   JOIN   tree t ON t.id = r.parent_id
   )
SELECT t.id, t.parent_id, t.depth
FROM   tree_list t
ORDER  BY t.id;

同じことをします。

見る

さて、これVIEWは些細なことです。

CREATE OR REPLACE VIEW v_tree15 AS
WITH RECURSIVE tree_list AS (
   SELECT t.id, t.parent_id, 1 AS depth
   FROM   tree t
   WHERE  t.id <= 15   -- only detail to change

   UNION ALL
   SELECT t.id, t.parent_id, r.depth + 1
   FROM   tree_list r
   JOIN   tree t ON t.id = r.parent_id
   )
SELECT t.id, t.parent_id, t.depth
FROM   tree_list t
ORDER  BY t.id;

結果は私にはあまり意味がありませんが、質問はより賢明なものを定義していません..

于 2012-11-15T08:35:25.983 に答える
2

次のようなものを使用できます。

CREATE OR REPLACE VIEW v_tree AS
SELECT tr.id as start,
       (_tree(tr.id)).id, 
       (_tree(tr.id)).parent_id, 
       (_tree(tr.id)).depth
FROM tree tr;

これは、すべてのノードからルートへのパスのビューです。

次に、次のようなものを使用します。

SELECT * 
FROM v_tree
WHERE start = 15;

希望のパスを取得します。

サンプルデータでは機能しますが、パフォーマンスのテストは行っていません。

クエリを_tree1回だけ呼び出すように更新しました:

CREATE OR REPLACE VIEW v_tree AS
SELECT t_tree.start,
       (t_tree.node).id, 
       (t_tree.node).parent_id, 
       (t_tree.node).depth
FROM (SELECT tr.id as start,
            _tree(tr.id) as node
      FROM tree tr) t_tree;
于 2012-11-15T15:50:00.017 に答える