2015-12-21 30 views
1

我有一个Oracle表格表示父子关系,并且我想提高查找祖先记录的层次结构的查询性能。我与小数据集在这里测试,但真正的桌子大得多:具有分层关系的SQL查询中的性能问题

id name parent_id tagged 
== ==== ========= ====== 
1 One  null  null 
2 Two  1   1 
3 Three 2   null 
4 Four 3   null 
5 Five null  null 
6 Six  5   1 
7 Seven 6   null 
8 Eight null  null 
9 Nine 8   null 

PARENT_ID指回id在同一个表的外键关系。

我想编写一个查询,它返回每个叶子记录(那些没有后代的记录...在这个例子中是id 4和id 7),它有一个祖先记录tagged = 1(通过parent_id关系回溯) 。

因此,对于上面的源数据,我想我的查询返回:

id name tagged_ancestor_id 
== ==== ================== 
4 Four 2 
7 Seven 6 

我目前的查询检索这些记录是:

select * from (
    select id, 
      name, 
      connect_by_root id tagged_ancestor_id  
    from mytree 
    connect by prior id = parent_id 
    start with tagged is not null 
) m1    
where not exists (
    select * from mytree m2 where m2.parent_id = m1.id 
) 

此查询工作正常在这个简单的小例如表格,但是在我的真实表格上它的表现非常糟糕,它有大约1100万条记录。查询需要一分钟才能运行。

  • connect by子句中的两个字段都有索引。
  • start with子句中的“tagged”字段也有一个索引,我的表中有大约1,500,000条记录,在这个字段中有非空值。
  • where子句似乎不是问题,因为修改它以使用where name = 'somename'而不是where not exists ...返回特定名称(也是索引的)时,查询仍然需要大约相同的时间量。

那么,我可以使用什么策略来尝试使这些层次结构上的查询运行得更快?

+0

如果您可以修改模式,则可以重新构建它以存储修改的预定义树遍历,但是需要添加2个附加列(左侧和右侧值)。几年前,我在一个我现在无法找到的教程中成功完成了这个任务。 (你应该得到一些额外的性能,但需要花费空间 – ozborn

+0

我确实可以控制模式,因此如果增加一些列是最好的方法来改善这种性能,那么就可以了。我想我可以把一个标记为祖先在自己的领域,并做到这一点,但似乎这个查询可以做得更快。 –

+0

这里是适当的维基百科参考:https://en.wikipedia.org/wiki/Nested_set_model我不知道我明白在其自己的领域藏匿一个标记的祖先会帮助你(除非你存储所有这些?),但我可能不理解你的问题...? – ozborn

回答

0

以下是我首先要检查的内容:

  1. 确保您的表具有主键。
  2. 确保表格上的统计数据是最新的。使用DBMS_STATS.GATHER_TABLE_STATS来收集统计信息。看到这个URL:(对于ORACLE版本11。1):

    http://docs.oracle.com/cd/B28359_01/appdev.111/b28419/d_stats.htm

  3. 即使你有两个字段的索引个人,你仍然需要

    在2场相结合的指标;在ID和PARENT_ID上创建索引:

    CREATE INDEX on TABLE_NAME(ID,PARENT_ID);

    看到这个网址:

    Optimizing Oracle CONNECT BY when used with WHERE clause

  4. 确保基础表没有行链接或其他问题(如贪污)。
  5. 确保表和所有索引位于同一个表空间中。

+0

谢谢你的建议,我会检查出来。 –

0

我不确定这是否更快没有数据量来测试......但需要考虑的事情。我想我只希望从那些被标记的那些开始,只有那些是叶子的我们正在处理一个较小的体积来处理这可能导致性能增益。但字符串操作的开销看起来很诡异。

with cte(id, name, parent_id, tagged) as (
SELECT 1, 'ONE', null, null from dual union all 
SELECT 2, 'TWO', 1, 1 from dual union all 
SELECT 3, 'THREE', 2, null from dual union all 
SELECT 4, 'FOUR', 3, null from dual union all 
select 5, 'FIVE', null, null from dual union all 
select 6, 'SIX', 5, 1 from dual union all 
select 7, 'SEVEN', 6, null from dual union all 
select 8, 'EIGHT', null, null from dual union all 
select 9, 'NINE', 8, null from dual), 

Leafs(id, name) as (select id, Name 
      from cte 
where connect_by_isleaf = 1 
Start with parent_Id is null 
connect by nocycle prior id =parent_id), 

Tagged as (SELECT id, name, SYS_CONNECT_BY_PATH(ID, '/') Path, substr(SYS_CONNECT_BY_PATH(ID, '/'),2,instr(SYS_CONNECT_BY_PATH(ID, '/'),'/',2)-2) as Leaf 
from cte 
where tagged=1 
start with id in (select id from leafs) 
connect by nocycle prior parent_id = id) 

select l.*, T.ID as Tagged_ancestor from leafs L 
inner join tagged t 
on l.id = t.leaf 

在本质上我在3 CTE的一个用于数据(CTE)一个用于叶子(叶子)和一个用于记录标记(标记)

我们遍历层次的两倍。一旦获得所有的叶子,一次获得所有的标签。然后,我们从标签层次结构中解析出第一个叶子值,并将其加回叶子以获取与标签记录相关的叶子。

至于这是不是比你在做什么,快... 耸肩我不想花时间的测试,因为我没有你的指标也没有告诉我你的数据量