计算其父项拥有的根的百分比

时间:2012-12-10 18:25:40

标签: sql oracle hierarchy oracle11gr2 connect-by

简单来说,我正在尝试计算其父项所拥有的树的根的百分比,在树的上方。我怎么能单独用SQL做这个?

这是我的(示例)架构。请注意,虽然层次结构本身非常简单,但还有一个holding_id,这意味着单个父母可以“拥有”他们孩子的不同部分。

create table hierarchy_test ( 
       id number -- "root" ID
     , parent_id number -- Parent of ID
     , holding_id number -- The ID can be split into multiple parts
     , percent_owned number (3, 2)
     , primary key (id, parent_id, holding_id) 
        );

以及一些示例数据:

insert all 
 into hierarchy_test values (1, 2, 1, 1) 
 into hierarchy_test values (2, 3, 1, 0.25)
 into hierarchy_test values (2, 4, 1, 0.25)
 into hierarchy_test values (2, 5, 1, 0.1)
 into hierarchy_test values (2, 4, 2, 0.4)
 into hierarchy_test values (4, 5, 1, 1)
 into hierarchy_test values (5, 6, 1, 0.3)
 into hierarchy_test values (5, 7, 1, 0.2)
 into hierarchy_test values (5, 8, 1, 0.5)
select * from dual;

SQL Fiddle

以下查询返回我想要进行的计算。由于SYS_CONNECT_BY_PATH的性质,据我所知,它本身不能执行计算。

 select a.*, level as lvl
      , '1' || sys_connect_by_path(percent_owned, ' * ') as calc
   from hierarchy_test a
  start with id = 1
connect by nocycle prior parent_id = id

数据中存在周期性关系,而不是在此示例中。

目前,我将使用一个非常简单的函数将calc列中的字符串转换为数字

create or replace function some_sum ( P_Sum in varchar2 ) return number is
   l_result number;
begin  
   execute immediate 'select ' || P_Sum || ' from dual'
     into l_result;

   return l_result;   
end;
/

这似乎是一种荒谬的方式,我宁愿避免在解析动态SQL 1 时花费额外的时间。

理论上,我认为,我应该能够使用MODEL子句来计算它。我的问题是由树的非唯一性引起的。我尝试使用MODEL子句执行此操作之一是:

select *
  from ( select a.*, level as lvl
              , '1' || sys_connect_by_path(percent_owned, ' * ') as calc
           from hierarchy_test a
          start with id = 1
        connect by nocycle prior parent_id = id
                 )
 model
 dimension by (lvl ll, id ii)
 measures (percent_owned, parent_id )
 rules upsert all ( 
   percent_owned[any, any]
   order by ll, ii  = percent_owned[cv(ll), cv(ii)] * nvl( percent_owned[cv(ll) - 1, parent_id[cv(ll), cv(ii)]], 1)
               )

这可以理解地失败了:

  

ORA-32638:MODEL维度中的非唯一寻址

使用UNIQUE SINGLE REFERENCE失败的原因类似,即ORDER BY子句不是唯一的。

TL;博士

是否有一种简单的方法可以仅使用SQL计算其父项所拥有的树的根百分比?如果我在使用MODEL的正确轨道上哪里出错?

<子> 1。我还想避免PL / SQL SQL上下文切换。我意识到这是一个很短的时间,但这很难在不增加每天额外几分钟的情况下快速完成。

2 个答案:

答案 0 :(得分:6)

在11g中,可能类似于 -

SELECT a.*, LEVEL AS lvl
      ,XMLQuery( substr( sys_connect_by_path( percent_owned, '*' ), 2 ) RETURNING CONTENT).getnumberval() AS calc
   FROM hierarchy_test a
  START WITH id = 1
CONNECT BY nocycle PRIOR parent_id = id;

SQL Fiddle

或者,根据您的'1'||技巧 -

SELECT a.*, LEVEL AS lvl
      , XMLQuery( ('1'|| sys_connect_by_path( percent_owned, '*' )) RETURNING CONTENT).getnumberval() AS calc
   FROM hierarchy_test a
  START WITH id = 1
CONNECT BY nocycle PRIOR parent_id = id;

不幸的是,在10g中,XMLQuery无法接受函数,并且总是需要一个字符串文字用于评估,例如 -

select XMLQuery('1*0.25' RETURNING CONTENT).getnumberval() as val 
  from dual;

工作并返回0.25,但

select XMLQuery(substr('*1*0.25',2) RETURNING CONTENT).getnumberval() as val
   from dual;

给出ORA-19102: XQuery string literal expected

随着XMLQuery本身内部树创建的额外开销,树上的级别数增加,查询可能会变慢。实现结果的最佳方法仍然是PL / SQL函数,顺便说一下,它可以在10g和11g中工作。

答案 1 :(得分:4)

这值得回答;虽然要注意我们在一些特殊情况下运作。

首先要提到的是,最好的方法是使用递归子查询因子/递归CTE,根据Daniel Hilgarth和评论中的jonearles:

with temp (id, parent_id, percent_owned, calc) as (
  select a.id, a.parent_id, a.percent_owned, percent_owned as calc
    from hierarchy_test a
   where id = 1
   union all
  select a.id, a.parent_id, a.percent_owned, a.percent_owned * t.calc as calc
    from temp t
    join hierarchy_test a
      on t.parent_id = a.id
         )
select * 
  from temp

Their SQL Fiddle.

不幸的是,查询的复杂性和我们正在处理的数据的大小使得这变得不可能。没有完全扫描每次过大的表格,就没办法做到这一点。

这并不一定意味着我们回到CONNECT BY。有机会批量计算层次结构。不幸的是,事实证明这也是不可能的;数据库中的一个小时崩溃了。三次。我们耗尽了近100GB的UNDO,服务器无法应付。

这些是特殊情况;我们必须在几个小时内计算出数十万个层次结构。平均值约为1.5级,可能有5-10个叶子,总共8-12个节点。但是,异常值具有90k节点,27个级别和多个循环关系。异常值并不是非常罕见。

所以,CONNECT BY。对问题中建议的PL / SQL EXECUTE IMMEDIATE进行基准测试Annjawn's solution表明,对于高于平均水平的树XMLQuery(),速度要慢4倍。很好,有答案;别无选择;留在那。

因为我们正在计算如此多的具有如此多节点的层次结构,所以我们最终得到了来自库缓存引脚锁的过长等待,这是由于{的数十万个数学函数的不断硬解析引起的。 {1}}。

对此没有明显的反应,所以回到Annjawn的解决方案,它最终会快3倍! 库缓存引脚锁完全消失,我们又回到了直线和窄端。

不幸的是,当您将EXECUTE IMMEDIATECONNECT BY和DBMS_SCHEDULER组合在一起时,11.2中似乎存在Oracle错误。在某些情况下,通常在较大的层次结构中,它会泄漏大量内存。丢失数据库服务器找到一个。已经与Oracle一起提交了一份报告,我们正在12c进行测试;虽然内存泄漏表现较少,但它们仍然出现,所以12c已经出局了。

解决方案?将XMLQuery()包装在PL / SQL函数中。内存泄漏已经解决,不幸的是,这导致了这个函数的大量争用,我们开始获得多小时库缓存:mutex x 等待。查询XMLQuery()确认它是{{1正在受到重创。

Andrey Nikolaev recommends改变系统;当其他一切正常时,或者使用DBMS_POOL.MARKHOT程序告诉Oracle你将要访问这个对象时,请不要这样做。顺便说一句,这可能已经解决了这个问题,但是,大约10分钟后,经历了几乎所有Oracle的锁定,我们最终得到了5个争用CPU的进程。显然还不够(测试盒上有54GB和24个核心)......

然后我们开始获取 Cursor pin:s 等待。 Burleson recommends更多隐藏参数finangling,Jonathan Lewis suggests表示SGA调整大小。由于数据库使用的是自动SGA大小调整,我们尝试逐渐增加共享池,最高可达30GB,并且只回到老朋友库缓存:mutex x 等待。

那么,解决方案是什么?谁知道这是诚实的答案,但到目前为止,Java存储过程的工作非常出色,没有内存泄漏,没有等待,而且比其他任何内容都快得多。

我确信那里还有更多......如果有人有任何想法,我真的想让x$kglob条款生效吗?

P.S。我不能要求所有这些;这是约3人的工作让我们进入这个阶段...