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

Ben*_*Ben 13 sql oracle connect-by hierarchy oracle11gr2

简单来说,我正在尝试计算其父项所拥有的树的根的百分比,进一步在树上.我怎么能单独用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) 
        );
Run Code Online (Sandbox Code Playgroud)

还有一些样本数据:

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;
Run Code Online (Sandbox Code Playgroud)

SQL小提琴

以下查询返回我想要进行的计算.由于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
Run Code Online (Sandbox Code Playgroud)

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

目前我将使用一个非常简单的函数将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;
/
Run Code Online (Sandbox Code Playgroud)

这似乎是一种荒谬的方式,我宁愿避免解析动态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)
               )
Run Code Online (Sandbox Code Playgroud)

可以理解,这可以通过以下方式失败:

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

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

TL;博士

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

1.我还想避免PL/SQL SQL上下文切换.我意识到这是一个很短的时间,但这很难做到很快,而不需要每天额外增加几分钟.

Ann*_*awn 6

在11克,可能类似于 -

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;
Run Code Online (Sandbox Code Playgroud)

SQL小提琴.

或者,根据你的'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;
Run Code Online (Sandbox Code Playgroud)

不幸的是,在10g中,XMLQuery不能接受函数并且总是期望用于评估的字符串文字例如 -

select XMLQuery('1*0.25' RETURNING CONTENT).getnumberval() as val 
  from dual;
Run Code Online (Sandbox Code Playgroud)

工作和回报0.25,但是

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

ORA-19102: XQuery string literal expected.

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


Ben*_*Ben 4

这值得一个答案;但请注意,我们是在一些特殊情况下运作的。

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

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
Run Code Online (Sandbox Code Playgroud)

他们的 SQL Fiddle。

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

这并不一定意味着我们又回到了CONNECT BY. 有机会批量计算层次结构。不幸的是,这也是不可能的。一个小时数据库崩溃了。三次。我们用掉了近 100GB 的 UNDO,而服务器根本无法应对。

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

所以,CONNECT BY将Annjaawn 的解决方案EXECUTE IMMEDIATE与问题中建议的PL/SQL进行基准测试表明,对于高于平均水平的树XMLQuery(),速度慢了 4 倍。太好了,有答案了;没有其他选择;就这样吧。

不是。

因为我们要使用如此多的节点计算如此多的层次结构,所以我们最终从库缓存引脚锁中获得了过长的等待,这是由于对EXECUTE IMMEDIATE.

对此没有明显的反应,所以回到 Annjan 的解决方案,结果快了 3 倍!库缓存引脚锁完全消失,我们又回到了正轨。

不是。

不幸的是,11.2 中似乎存在一个 Oracle 错误,当您将CONNECT BY,XMLQuery()和 DBMS_SCHEDULER 组合使用时,会出现该错误。在某些情况下,通常在较大的层次结构中,它会泄漏大量内存。数据库丢失服务器找到了该数据库。已向 Oracle 提交了一份报告,我们正在 12c 中进行测试;尽管内存泄漏表现得较少,但它们仍然出现,因此 12c 已被淘汰。

解决方案?将 包装XMLQuery()在 PL/SQL 函数中。内存泄漏已解决,不幸的是,这导致了该函数的大量争用,并且我们开始获得数小时的库缓存:互斥体 x等待..查询x$kglob证实它XMLTYPE正在受到重击。

安德烈·尼古拉耶夫建议要么改变系统;要么 当其他一切正常时,不要这样做,或者使用该DBMS_POOL.MARKHOT过程告诉 Oracle 您将经常访问该对象。乍一看,这可能已经解决了问题,但是,大约 10 分钟后,并检查了 Oracle 拥有的所有锁后,我们最终发现有 5 个进程争夺 CPU。显然还不够(测试盒上有 54GB 和 24 个核心)...

然后我们开始等待Cursor pin:sBurleson 建议使用更多隐藏参数,Jonathan Lewis 建议调整 SGA 大小。由于数据库使用自动 SGA 大小调整,我们尝试逐渐增加共享池,最高可达 30GB,但只恢复了老朋友库缓存:互斥体 x等待。

那么,解决办法是什么呢?谁知道这是诚实的答案,但 Java 存储过程到目前为止运行得非常出色,没有内存泄漏,没有等待,并且比其他任何东西都快得多。

我确信还有更多...MODEL如果有人有任何想法,我真的很想让该条款发挥作用?

PS:这一切都不能归功于我;大约有 3 个人的努力让我们达到了这个阶段......