修改递归查询以将结果限制为树的第一个匹配节点

时间:2013-12-22 10:03:04

标签: sql postgresql recursion

我有来自acts_as_ordered_tree gem的这个SQL输出:

SELECT "component_instances".* 
FROM "component_instances" 
INNER JOIN (
   WITH RECURSIVE descendants AS ( 
      SELECT id, parent_id, ARRAY[position] AS _positions
      FROM "component_instances"
      WHERE "component_instances"."id" = 1
      UNION ALL
      SELECT alias1.id, alias1.parent_id, _positions || alias1.position
      FROM descendants 
      INNER JOIN "component_instances" alias1 
              ON alias1.parent_id = descendants.id
   ) 
   SELECT * FROM descendants
) AS descendants 
ON descendants.id = component_instances.id 
WHERE ("component_instances"."id" != 1) 
  AND (component_instances.archived_at IS NOT NULL 
  AND component_instances.archive_number IS NOT NULL) 
ORDER BY descendants._positions ASC

此查询用于回收站,我想在其中显示已“存档”的component_instances(请参阅查询末尾的最后一个条件)。即使它们也被标记为已存档,我也不想显示存档的component_instance的子节点,因为除非您先恢复了最初删除的父节点,否则无法在逻辑上还原这些节点。

我无法理解修改此查询,以便在遇到存档节点时不会递归,但仍会在结果中包含该节点。

1 个答案:

答案 0 :(得分:0)

我不太确定我是否理解这个问题,
我将尝试使用一个示例来展示我的理解:

假设表component_instances具有以下记录:

| ID | PARENT_ID | POSITION | ARCHIVED_AT | ARCHIVE_NUMBER |
|----|-----------|----------|-------------|----------------|
|  1 |    (null) |   (null) |      (null) |         (null) |
|  2 |         1 |   (null) |      (null) |         (null) |
|  3 |         1 |   (null) |           1 |              1 | <== archived
| 21 |         2 |   (null) |      (null) |         (null) |
| 22 |        21 |   (null) |      (null) |         (null) |
| 31 |         3 |   (null) |      (null) |         (null) | <== children of 3
| 32 |        31 |   (null) |      (null) |         (null) | <== children of 3

根据我的理解,即使它是“存档的”,查询也应该检索记录id=3
但必须跳过记录id=31id=32,因为有“存档”记录id=3的子项。

如果我的理解是正确的,那么在SELECT子句中附加一个列archived_at
在连接后检查父记录的archived_at是否为空:

WITH RECURSIVE descendants AS ( 
      SELECT id, parent_id, ARRAY[position] AS _positions,
             /* new column */
             archived_at
      FROM "component_instances"
      WHERE "component_instances"."id" = 1
      UNION ALL
      SELECT alias1.id, alias1.parent_id, _positions || alias1.position,
             /* new column */
             alias1.archived_at
      FROM descendants
      INNER JOIN "component_instances" alias1 
              ON alias1.parent_id = descendants.id
      /* new condition - the parent record is not archived */
      WHERE descendants.archived_at is null
) 
SELECT * FROM descendants
;

demo(参见本演示中的最后一个查询) - &gt; http://www.sqlfiddle.com/#!15/033a7/6