Sql 处理递归(oracle)
我在尝试为客户网络交易做一些业务逻辑时遇到了一些问题: 我的客户有一个简化的表格,如下所示:Sql 处理递归(oracle),sql,oracle,oracle10g,hierarchical-data,recursive-query,Sql,Oracle,Oracle10g,Hierarchical Data,Recursive Query,我在尝试为客户网络交易做一些业务逻辑时遇到了一些问题: 我的客户有一个简化的表格,如下所示: || ID || OFFERED_PRODUCT_ID || DEMANDED_PRODUCT_ID || || 01 || 34 || 45 || || 01 || 34 || 16 || || 01 || 45 || 57
|| ID || OFFERED_PRODUCT_ID || DEMANDED_PRODUCT_ID ||
|| 01 || 34 || 45 ||
|| 01 || 34 || 16 ||
|| 01 || 45 || 57 ||
|| 01 || 47 || 57 ||
|| 01 || 57 || 63 ||
|| 01 || 16 || 20 ||
现在,应用程序必须显示最大的贸易链,贸易链定义为提供和需求产品之间的关系
这是贸易链:
<34,45,57,63>
我试图用C语言来解决这个问题,但由于数据量太大,这就变得不可能了。我的一位同事建议我使用递归。我试图理解他的意思,但我是一名客户端开发人员,对SQL不太了解
我做了这些:
select o.OFFERED_PRODUCT_ID, o.DEMANDED_PRODUCT_ID
from TRADES o
start with
o.DEMANDED_PRODUCT_ID = (SELECT MIN(o.DEMANDED_PRODUCT_ID) from TRADES)
connect by NOCYCLE prior o.DEMANDED_PRODUCT_ID = o.OFFERED_PRODUCT_ID;
我试图用系统上最早的产品和最小标识符来启动递归。但它不起作用。它给了我所有的贸易链。我只需要最大的一个
下面是一个输出示例:
OFFERED_PRODUCT_ID DEMANDED_PRODUCT_ID
9920896475501851 59587794888502550724
59587794888502550724 13197303523502765990
13197303523502765990 54010274740204405159
54010274740204405159 14505831337880766413
14505831337880766413 89607128670993987443
89607128670993987443 8802863939059413452
8802863939059413452 7779127922701247342
7779127922701247342 3810800421539873909
3810800421539873909 12423373218147473557
交易链的长度等于递归的深度。Oracle有一个psuedo变量LEVEL,您可以使用它。如果您接受原始查询并按级别排序,则最长链的末尾将位于第一位。然后我们需要消除查询的其余部分。 上面的命令将返回链中的最后一行。要在逗号分隔的列表中获取整个链,请执行以下操作: 您可能需要删除前导逗号。 如果您想要最长的链,一次一行: select c.OFFERED_PRODUCT_ID, c.DEMANDED_PRODUCT_ID from TRADES c start with (c.OFFERED_PRODUCT_ID, c.DEMANDED_PRODUCT_ID) in ( select o1, d1 from ( select level lvl, o.OFFERED_PRODUCT_ID, o.DEMANDED_PRODUCT_ID , CONNECT_BY_ROOT OFFERED_PRODUCT_ID o1 , CONNECT_BY_ROOT DEMANDED_PRODUCT_ID d1 from TRADES o start with o.OFFERED_PRODUCT_ID not in (select st.DEMANDED_PRODUCT_ID from trades st) connect by NOCYCLE prior o.DEMANDED_PRODUCT_ID = o.OFFERED_PRODUCT_ID order by level desc ) where rownum = 1 ) connect by NOCYCLE prior c.DEMANDED_PRODUCT_ID = c.OFFERED_PRODUCT_ID
CONNECT_BY_ROOT命令从递归的根(即链的第一行)获取数据。我正在运行10g Rel 2。我不确定它是否在旧版本上可用。我相信你想要类似的东西
SQL> ed
Wrote file afiedt.buf
1 with trades as (
2 select 34 offered_product_id, 45 demanded_product_id from dual
3 union all
4 select 34, 16 from dual
5 union all
6 select 45, 57 from dual
7 union all
8 select 47, 57 from dual
9 union all
10 select 57, 63 from dual
11 union all
12 select 16, 20 from dual
13 )
14 select path
15 from (
16 select offered_product_id,
17 demanded_product_id,
18 level + 1,
19 sys_connect_by_path( offered_product_id, '/' ) ||
20 '/' || demanded_product_id path,
21 dense_rank() over (order by level desc) rnk
22 from trades
23 connect by prior demanded_product_id = offered_product_id )
24* where rnk = 1
SQL> /
PATH
--------------------
/34/45/57/63
因为o。将其与主查询关联。这相当于以o开头。提供的产品ID不为空。我想你想要:
以上将给你所有的链条,但不会启动任何链条在中间。如果你只想要最长的链子,你显然不想从中间开始短的链条。 我想你可能需要换一下你的前任在哪里?如中所示:通过NOCYCLE o.REQUIRED_PRODUCT_ID=先前o.REQUIRED_PRODUCT_ID连接;没有。它与实际订单一起工作。只不过它不会返回最长的值。+1 select语句是一个很好的答案,并且提供的with子句可以方便地模拟真实的db数据。感谢您的回答。但我不能尝试。它说:ORA-01489:字符串连接的结果太长01489。00000-字符串连接结果太长*原因:字符串连接结果超过最大大小*措施:确保结果小于最大值。@Randolf-你的实际交易链有多长?从错误中听起来,SYS\u CONNECT\u BY\u PATH试图生成长度超过4000个字符的字符串。您可以取消该调用,在这种情况下,您将无法再获得路径,或者您可以找到其他方法来表示路径—可能有一个具有较短标识符的PRODUCT表,您可以加入?。不。没有一个:如果我删除SYS\u CONNECT\u BY\u路径,那么它开始执行,但从不停止。我添加了NOCYCLE子句,但即使这样它也没有停止:Define没有停止。我认为TRADES表可能相当大——查询可能需要一段时间才能运行。Hi。它开始执行,但没有任何响应。我正在本地局域网内尝试。只有20k个条目:S@RandolfR-F:检查您输入的对账单。当我将level添加到select列表中,并根据您在问题中给出的数据运行它时,它将返回一行3 57 63。这是最长链条的最后一行。但是你想要整个链条,对吗? select c.OFFERED_PRODUCT_ID, c.DEMANDED_PRODUCT_ID from TRADES c start with (c.OFFERED_PRODUCT_ID, c.DEMANDED_PRODUCT_ID) in ( select o1, d1 from ( select level lvl, o.OFFERED_PRODUCT_ID, o.DEMANDED_PRODUCT_ID , CONNECT_BY_ROOT OFFERED_PRODUCT_ID o1 , CONNECT_BY_ROOT DEMANDED_PRODUCT_ID d1 from TRADES o start with o.OFFERED_PRODUCT_ID not in (select st.DEMANDED_PRODUCT_ID from trades st) connect by NOCYCLE prior o.DEMANDED_PRODUCT_ID = o.OFFERED_PRODUCT_ID order by level desc ) where rownum = 1 ) connect by NOCYCLE prior c.DEMANDED_PRODUCT_ID = c.OFFERED_PRODUCT_ID
SQL> ed
Wrote file afiedt.buf
1 with trades as (
2 select 34 offered_product_id, 45 demanded_product_id from dual
3 union all
4 select 34, 16 from dual
5 union all
6 select 45, 57 from dual
7 union all
8 select 47, 57 from dual
9 union all
10 select 57, 63 from dual
11 union all
12 select 16, 20 from dual
13 )
14 select path
15 from (
16 select offered_product_id,
17 demanded_product_id,
18 level + 1,
19 sys_connect_by_path( offered_product_id, '/' ) ||
20 '/' || demanded_product_id path,
21 dense_rank() over (order by level desc) rnk
22 from trades
23 connect by prior demanded_product_id = offered_product_id )
24* where rnk = 1
SQL> /
PATH
--------------------
/34/45/57/63
select level, o.OFFERED_PRODUCT_ID, o.DEMANDED_PRODUCT_ID, SYS_CONNECT_BY_PATH(o.OFFERED_PRODUCT_ID, ','), SYS_CONNECT_BY_PATH(o.DEMANDED_PRODUCT_ID, ',')
, CONNECT_BY_ROOT OFFERED_PRODUCT_ID o1
, CONNECT_BY_ROOT DEMANDED_PRODUCT_ID d1
from TRADES o
start with o.OFFERED_PRODUCT_ID not in (select st.DEMANDED_PRODUCT_ID from trades st)
connect by NOCYCLE prior o.DEMANDED_PRODUCT_ID = o.OFFERED_PRODUCT_ID