如何在Postgresql中以条件递归获取记录?

时间:2015-12-31 06:04:53

标签: sql postgresql recursive-query

我有两张桌子:

locations

locationid   parentid
  1
  2              1
  3              1
  4              2
  5              4
  8              1

approvelog

locationid  approved
   4          True
   8          True

我需要编写一个查询,该查询提供特定位置的所有子位置,但如果locationid被批准,则忽略它及其子项,即使这些子项未获批准也是如此。

简单来说,当遇到具有approved=True的locationid时,忽略它及其所有子项(意味着停止递归此分支)。

例如:

  • 我希望获得locationid=2

    2
    
  • 我希望获得locationid=8

    Nothing
    
  • 我希望获得locationid=1

    1,2,3
    

    4被批准所以忽略它和它的孩子。 8被批准,所以忽略它。

这是我的代码:

with recursive location_tree as (
   select  locationid, parentid
   from locations
   where locationid = 1 and not approved
   union all
   select child.locationid, child.parentid
   from locations child
   join location_tree parent on parent.locationid = child.parentid
   where child.active
)
select array_agg(locationid) 
from location_tree

这只是给出一个位置列表。

基本上我需要的是递归的“停止条件”。 如何更改它?

有人可以帮忙吗?

2 个答案:

答案 0 :(得分:1)

这是我在PostgreSQL中用来让父母及其未经批准的孩子认为父母本身也未被批准的内容:

WITH recursive location_tree AS (
  --PARENT
  SELECT
    p.locationid,
    p.parentid
  FROM
    locations p
  LEFT JOIN
    approvelog pa ON pa.locationid = p.locationid AND pa.approved = TRUE
  WHERE
    p.locationid = 1
    AND pa.locationid IS NULL --Exclude approved parent
  UNION ALL

  --CHILD
  SELECT 
    c.locationid,
    c.parentid
  FROM 
    locations c
  JOIN 
    location_tree p on p.locationid = c.parentid
  LEFT JOIN
    approvelog ca ON ca.locationid = c.locationid AND ca.approved = TRUE
  WHERE
    ca.locationid IS NULL --Exclude approved children
)
SELECT 
  array_agg(locationid) 
FROM 
  location_tree

SQL小提琴:http://sqlfiddle.com/#!15/7084f/19

答案 1 :(得分:0)

我相信@ vanlee1987的答案很好。或者,您可以将其包装在函数中,并在函数本身内执行实际递归(而不是递归SQL)。例如:

CREATE OR REPLACE FUNCTION location_tree(location_id integer)
  RETURNS integer[] AS
$BODY$
DECLARE
  result integer[];
  r locations%rowtype;
BEGIN

  select array[l.locationid]
  into result
  from
    locations l
    left join approvelog a on
      l.locationid = a.locationid
  where
    l.locationid = location_id and
   (a.approved is null or a.approved = false);

  for r in
    select
      l.locationid, l.parentid
    from
      locations l
    where
      l.parentid = any (result)
  loop
    result := array_cat(result, location_tree(r.locationid));
  end loop;

  return result;
end
$BODY$
  LANGUAGE plpgsql VOLATILE
  COST 100;

实现:

select location_tree(1);     // yields {1,2,3}
select location_tree(2);     // yields {2}
select location_tree(8);     // yields {}