Oracle 嵌套 CONNECT BY 子句导致性能不佳

Posted

技术标签:

【中文标题】Oracle 嵌套 CONNECT BY 子句导致性能不佳【英文标题】:Oracle nested CONNECT BY clauses causing poor performance 【发布时间】:2019-02-08 18:28:11 【问题描述】:

下面的查询大约需要一分钟。我相信性能不佳是由两个“IN(SELECT ...”子句引起的。我有一个术语表,其中一个可以通过 term_relationship 表连接到另一个。这些关系可以是递归的,例如 dog 是一种哺乳动物,哺乳动物是一种动物。这种递归可以是任何深度,但可能不超过 10 个级别。我正在尝试选择所有与 A 型具有(潜在递归)关系并具有(潜在递归)关系的术语使用 B 类型。我认为用外部查询的限制替换两个“IN (SELECT ...”子句会提高性能,但无法弄清楚如何使用 CONNECT BY 子句来做到这一点。有人可以帮忙吗?

SELECT term_name
FROM term
WHERE term_id IN 
   (SELECT term_id 
    FROM term_relationship 
    START WITH related_term_id = 123
    CONNECT BY NOCYCLE PRIOR term_id = related_term_id)
AND term_id IN 
   (SELECT term_id 
    FROM term_relationship 
    START WITH related_term_id = 456
    CONNECT BY NOCYCLE PRIOR term_id = related_term_id)

【问题讨论】:

【参考方案1】:

与其只使用不同的起始值执行相同的CONNECT BY 查询两次,不如通过将两个起始值都提供给子查询的一个实例来执行一次。此更改将为您提供与您的任一起始值相关的所有 term_ids,但是,您只需要与您的两个起始值相关的 term_ids。为此,您需要按 term_id 对结果进行分组,并限制为计数超过 1 的结果:

SELECT term_name
  FROM term
 WHERE term_id IN 
    (SELECT term_id 
       FROM term_relationship 
      START WITH related_term_id in (123, 456)
    CONNECT BY NOCYCLE PRIOR term_id = related_term_id
      group by term_id having count(*) >= 2)

编辑 使用上面的代码,我对您的数据做出了一个可能不正确的假设。我假设一个树状结构,您从分支上的节点开始,然后像图 A​​ 中那样向根部移动,但是,如果您的数据看起来像图 B,那么如果您从节点 7 和 9 开始,上述查询将失败由于节点 7 有两条路径返回节点 1,上述查询将返回节点 1 两次,从而将其误识别为公共节点。

A)   -(1)-                    B)   -(1)-
    /  |  \     (8)               /  |  \     (8)
  (2)  |  (3)    |              (2)  |  (3)    |
   |  (4)  |    (9)              |  (4)  |    (9)
  (5)     (6)                   (5)     (6)
           |                      \     /
          (7)                      -(7)-

下面的查询对此进行了更正,并将正确识别出对于起始节点 7 和 9,没有节点是共同的,但是,对于起始节点 7 和 4,节点 1 被识别为共同节点:

SELECT term_name
  FROM term
 WHERE term_id IN 
    (SELECT term_id 
     FROM term_relationship 
    START WITH related_term_id in (123, 456)
  CONNECT BY NOCYCLE PRIOR term_id = related_term_id
    group by term_id
   having count(distinct connect_by_root related_term_id) >= 2)

【讨论】:

感谢您的回复。数据看起来像图 B。不幸的是,为子查询的一个实例提供两个起始值会降低性能。我用两个真实的起始值对其进行了测试。原始查询耗时 26 秒,带有单个子查询的查询耗时 33 秒。我认为这是因为查询优化器正在运行子查询,该子查询将在运行另一个子查询之前删除最多的行。

以上是关于Oracle 嵌套 CONNECT BY 子句导致性能不佳的主要内容,如果未能解决你的问题,请参考以下文章

Connect By 子句适用于 11g,但不适用于 Oracle 8i:“ORA-01436:用户数据中的 CONNECT BY 循环”

Oracle 之 树查询 START WITH ... CONNECT BY ...子句

Oracle中start with...connect by子句的用法

在 Oracle 中使用 connect by 在 with 子句中访问子查询中的父别名

如何从表中选择带有 oracle sql 中的 group by 子句的嵌套 json 对象?

Oracle “CONNECT BY” (层级递归查询)