4

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

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
4

3 回答 3

2

执行查询需要多长时间,您希望它需要多长时间?(您通常不想使用成本进行调整。很少有人知道解释计划成本的真正含义。)

在我的慢速桌面上,查询 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;
于 2011-03-12T06:13:27.587 回答
0

您可以尝试在距离和 child_node_id 上添加索引,或者更改这些列在现有唯一索引中的顺序吗?我认为外部查询应该可以按距离通过索引访问表,而内部查询只需要访问索引。

于 2011-03-11T19:15:01.490 回答
0

添加一个根节点,所有当前根节点都从该根节点继承。然后你会简单地查询你的一个根的孩子。问题解决了。

于 2012-11-06T12:35:19.987 回答