从函数返回多个ID并在查询中使用结果

时间:2012-09-10 16:43:16

标签: sql postgresql plpgsql postgresql-9.1

我有这个函数,它返回一个树节点的所有子节点:

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}'

2 个答案:

答案 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);

这应该快一点。