简单来说,我正在尝试计算其父项所拥有的树的根的百分比,在树的上方。我怎么能单独用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;
以下查询返回我想要进行的计算。由于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子句不是唯一的。
是否有一种简单的方法可以仅使用SQL计算其父项所拥有的树的根百分比?如果我在使用MODEL的正确轨道上哪里出错?
<子> 1。我还想避免PL / SQL SQL上下文切换。我意识到这是一个很短的时间,但这很难在不增加每天额外几分钟的情况下快速完成。
答案 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;
或者,根据您的'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
不幸的是,查询的复杂性和我们正在处理的数据的大小使得这变得不可能。没有完全扫描每次过大的表格,就没办法做到这一点。
这并不一定意味着我们回到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 IMMEDIATE
,CONNECT 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人的工作让我们进入这个阶段...