Oracle 连接包括一个停止条件

Oracle connect by including a stop criteria

有一个 table articles 包括分层文章结构。 1 个组件由 n 个组件组成。所以我们可以浏览文章的结构和用法(up and down)。

使用 Oracles hierarchical queries 这可以在 sql 水平上非常有效地完成。

SELECT item
FROM articles
START WITH component = '0815'
CONNECT BY NOCYCLE PRIOR assembly = component;

假设有一个文章螺丝。该螺钉用于许多组件以及它们的组件。我们想弄清楚 srew 是否用于由 WHERE 子句标识的特定组件 above.

SELECT item
FROM articles
WHERE attr1 = 'marker' --any condition
START WITH component = '0815'
CONNECT BY NOCYCLE PRIOR assembly = component;

此语句效果很好,但会评估结果中所有可能的程序集。在我们的例子中,我们只对是否有 至少一个程序集匹配 而不是整个结果感兴趣。该语句对所有程序集都需要几分钟,但当它在第一行之后停止回答给​​定问题时可能会更快。

有没有办法告诉 Oracle 在第一次匹配后中止此查询?

您可以使用递归子查询分解来停止所有这样的搜索:

with h(it,art,match,anymatch) as
       (select item, assembly
             ,     case when attr1 = 'marker' then 1 else 0 end
             , max(case when attr1 = 'marker' then 1 else 0 end) over()
          from articles
         where component = '0815'
        union all
        select item, assembly
             ,     case when attr1 = 'marker' then 1 else 0 end
             , max(case when attr1 = 'marker' then 1 else 0 end) over()
          from h, articles
         where art = component
           and anymatch = 0)
cycle art set cycle to 1 default 0
select it item
  from h
 where match = 1
   and cycle = 0

它将 return 在尽可能小的级别上找到的所有匹配项。

然而,由于是广度优先搜索,如果第一个找到的 marker 很深,它不会快很多。

将条件 anymatch = 0 更改为 match = 0(不再需要计算 anymatch)将停止仅向下搜索匹配所在的分支。

要进行真正的 depth-first 搜索,您可以使用以下 PL/SQL:

FUNCTION search(p_component IN VARCHAR2, p_attr1 IN VARCHAR2)
RETURN VARCHAR2 IS
  i VARCHAR2(4000);
BEGIN
  FOR q IN (SELECT * FROM articles WHERE component = p_component)
  LOOP
    IF q.attr1 = p_attr1 THEN
      RETURN q.item;
    END IF;
    i := search(q.assembly, p_attr1);
    IF i IS NOT NULL THEN
      RETURN i;
    END IF;
  END LOOP;
  RETURN NULL;
END;

你这样调用函数:

search('0815', 'marker')

我的猜测是,如果 marker 根本不出现,此解决方案将慢得多。它也不检查循环,并在有限的水平上工作(打开游标或调用堆栈的限制可能会耗尽)。

Oracle 12 中,您可以将 PL/SQL 放入 SQL:

WITH
  FUNCTION search(p_component IN VARCHAR2, p_attr1 IN VARCHAR2)
  RETURN VARCHAR2 IS
    i VARCHAR2(4000);
  BEGIN
    FOR q IN (SELECT * FROM articles WHERE component = p_component)
    LOOP
      IF q.attr1 = p_attr1 THEN
        RETURN q.item;
      END IF;
      i := search(q.assembly, p_attr1);
      IF i IS NOT NULL THEN
        RETURN i;
      END IF;
    END LOOP;
    RETURN NULL;
  END;
SELECT search('0815', 'marker')
  FROM dual