我有这个函数,它返回一个树节点的所有子节点:
CREATE OR REPLACE FUNCTION fn_category_get_childs_v2(id_pai integer)
RETURNS integer[] AS
$BODY$
DECLARE
ids_filhos integer array;
BEGIN
SELECT array (
SELECT category_id FROM category WHERE category_id IN (
(WITH RECURSIVE parent AS
(
SELECT category_id , parent_id from category WHERE category_id = id_pai
UNION ALL
SELECT t.category_id , t.parent_id FROM parent
INNER JOIN category t ON parent.category_id = t.parent_id
)
SELECT category_id FROM parent
WHERE category_id <> id_pai
) )
) into ids_filhos;
return ids_filhos;
END;
我想在这样的选择语句中使用它:
select *
from teste1_elements
where category_id in (select * from fn_category_get_childs_v2(12))
我也尝试过这种方式,结果相同:
select *
from teste1_elements
where category_id=any(select * from fn_category_get_childs_v2(12)))
但是我收到以下错误:
ERROR: operator does not exist: integer = integer[]
LINE 1: select * from teste1_elements where category_id in (select *...
^
HINT: No operator matches the given name and argument type(s). You might need to add explicit type casts.
该函数返回一个整数数组,那是什么问题?
SELECT * from fn_category_get_childs_v2(12)
检索以下数组(integer[]
):
'{30,32,34,20,19,18,17,16,15,14}'
答案 0 :(得分:2)
该函数返回一个整数数组,那是问题吗?
是。通常,如果要使用where category_id in (select * from fn_category_get_childs_v2(12))
,则需要函数返回一组行,而不是数组。像RETURNS SETOF integer
example in the manual可能有所帮助:
也可以使用现有的函数,但是你必须使用不同的语法来比较数组。
我认为这应该更接近:
select *
from teste1_elements
where category_id = any(fn_category_get_childs_v2(12))
答案 1 :(得分:1)
核心的递归WITH
查询可以简化为:
WITH RECURSIVE children AS (
SELECT category_id
FROM category
WHERE parent_id = id_pai
UNION ALL
SELECT c.category_id
FROM children ch
JOIN category c ON c.parent_id = ch.category_id
)
SELECT *
FROM children;
更短,更清洁,更快 如果你想将它包装成一个函数,我会在这个简单的例子中使用普通SQL function而不是plpgsql function:
CREATE OR REPLACE FUNCTION f_cat_children(_id_pai int, OUT category_id int)
RETURNS SETOF int LANGUAGE SQL AS
$func$
WITH RECURSIVE children AS (
SELECT c.category_id
FROM category c
WHERE c.parent_id = $1
UNION ALL
SELECT c.category_id
FROM children ch
JOIN category c ON c.parent_id = ch.category_id
)
SELECT *
FROM children;
$func$;
我使用OUT
parameter将列名category_id
分配给结果列 - 稍后简化了连接。请注意,这在身体的任何地方都可见。这就是为什么你必须对表格c.category_id
进行限定才能使其明确无误。
回复你的评论
自PostgreSQL 9.2(昨天发布)以来,您可以按名称或位置参数($1
,$2
,...)引用参数。我引用手册here:
可以在函数体中引用SQL函数的参数 使用名称或数字。两种方法的例子如下所示。
但是,我在这里太兴奋了,因为PostgreSQL 9.1或更早版本的情况并非如此,在函数体中只能理解位置参数。所以,我的不好,我相应修改了这个功能。
PL / pgSQL函数已经理解了参数 names ,因为它们是在8.0版本中引入的。
您生成的查询可以简单地写为(使用JOIN
语法而不是IN
):
SELECT t.*
FROM teste1_elements t
JOIN f_cat_children(12) USING (category_id)
在PostgreSQL中加入表通常比x IN (SELECT y FROM ...)
更快,特别是对于更大的列表。
或者你可以在没有函数的情况下在一个查询中执行所有:
WITH RECURSIVE children AS (
SELECT category_id
FROM category
WHERE parent_id = id_pai
UNION ALL
SELECT c.category_id
FROM children ch
JOIN category c ON c.parent_id = ch.category_id
)
SELECT t.*
FROM children
JOIN teste1_elements t USING (category_id);
这应该快一点。