sql – 如何加快寻找传递闭包的根节点的查询?

我有一个代表树的历史传递闭包表.

create table TRANSITIVE_CLOSURE
  (
    CHILD_NODE_ID number not null enable,
    ANCESTOR_NODE_ID number not null enable,
    DISTANCE number not null enable,
    FROM_DATE date not null enable,
    TO_DATE date not null enable,
    constraint TRANSITIVE_CLOSURE_PK unique (CHILD_NODE_ID, ANCESTOR_NODE_ID, DISTANCE, FROM_DATE, TO_DATE)
  );

这是一些示例数据:

CHILD_NODE_ID | ANCESTOR_NODE_ID | DISTANCE 
--------------------------------------------
1             | 1                | 0
2             | 1                | 1
2             | 2                | 0
3             | 1                | 2
3             | 2                | 1
3             | 3                | 0

不幸的是,我当前查找根节点的查询会导致全表扫描:

select *
from transitive_closure tc
where 
  distance = 0
  and not exists (
  select null
  from transitive_closure tci
  where tc.child_node_id = tci.child_node_id
    and tci.distance <> 0
);

从表面上看,它看起来并不太昂贵,但随着我接近100万行,这个特定的查询开始变得令人讨厌……特别是当它是一个视图的一部分,它抓住了邻接树以获得遗留支持.

有没有更好的方法来找到传递闭包的根节点?我想重写所有旧的遗留代码,但我不能……所以我需要以某种方式构建邻接列表.获取除根节点之外的所有内容都很简单,那么有更好的方法吗?我是否以错误的方式思考这个问题?

在具有800k行的表上查询计划.

OPERATION                                  OBJECT_NAME        OPTIONS         COST 
SELECT STATEMENT                                                              2301 
  HASH JOIN                                                   RIGHT ANTI      2301 
    Access Predicates
      TC.CHILD_NODE_ID=TCI.CHILD_NODE_ID 
    TABLE ACCESS                           TRANSITIVE_CLOSURE FULL            961 
      Filter Predicates 
        TCI.DISTANCE = 1 
    TABLE ACCESS                           TRANSITIVE_CLOSURE FULL            962 
      Filter Predicates 
        DISTANCE=0

最佳答案 查询执行需要多长时间,以及您需要多长时间? (您通常不希望使用调整成本.很少有人知道解释计划成本的真正含义.)

在我的慢桌面上,对于800K行,查询只花了1.5秒.然后在数据存储器中0.5秒后.你得到的事情会变得更糟吗?
或者这个查询会经常运行吗?

我不知道你的数据是什么样的,但我猜想全表扫描总是最适合这个查询.假设您的分层数据
相对较浅,即有很多距离为0和1但距离很短的100,最重要的列不会非常明显.这意味着
距离的任何索引条目都将指向大量的块.使用多块读取一次读取整个表会便宜得多
而不是一次读一大块一块.

另外,你的历史是什么意思?您可以在物化视图中存储此查询的结果吗?

另一个可能的想法是使用分析函数.这将使用排序替换第二个表扫描.这种方法通常更快,但对我而言
查询实际需要更长时间,5.5秒而不是1.5秒.但也许它会在你的环境中做得更好.

select * from
(
    select
        max(case when distance <> 0 then 1 else 0 end)
            over (partition by child_node_id) has_non_zero_distance
        ,transitive_closure.*
    from transitive_closure
)
where distance = 0
    and has_non_zero_distance = 0;
点赞