我正在尝试将Postgres中的函数转换为select
查询,我打算将其用作视图。原因是我想通过带有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子句是否会被视为)?
答案 0 :(得分: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,可能略微有利于在PostgreSQL 9.2之前的版本中兑现查询计划。但是,您无需使用动态SQL就无法获得唯一的理论上的好处。这毫无意义。简化为纯SQL。
使用UNION ALL
代替UNION
,更便宜,因为设计不会出现欺骗行为。
显然,你可以用普通的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;
结果对我来说没有多大意义,但问题并没有定义更明智的事情。
答案 1 :(得分: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;
获得理想的路径。
它对我的示例数据有用,但我还没有测试它的性能。
更新了只调用_tree
一次的查询:
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;