oracle递归避免循环
oracle recursive avoid cycle
以下查询是在 https://www.tutorialspoint.com/execute_sql_online.php 上开发的并且按设计工作:
WITH helptab1
AS (SELECT "a" AS cust,
1 AS numb,
"m006" AS val
FROM dual
UNION ALL
SELECT "b" AS cust,
1 AS numb,
"m006" AS val
FROM dual
UNION ALL
SELECT "b" AS cust,
1 AS numb,
"m777" AS val
FROM dual
UNION ALL
SELECT "b" AS cust,
2 AS numb,
"m018" AS val
FROM dual
UNION ALL
SELECT "c" AS cust,
2 AS numb,
"m454" AS val
FROM dual
UNION ALL
SELECT "c" AS cust,
5 AS numb,
"m008" AS val
FROM dual
UNION ALL
SELECT "c" AS cust,
5 AS numb,
"m090" AS val
FROM dual
UNION ALL
SELECT "c" AS cust,
6 AS numb,
"m789" AS val
FROM dual
UNION ALL
SELECT "c" AS cust,
7 AS numb,
"m191" AS val
FROM dual
UNION ALL
SELECT "d" AS cust,
9 AS numb,
"m006" AS val
FROM dual
UNION ALL
SELECT "d" AS cust,
1 AS numb,
"m123" AS val
FROM dual
UNION ALL
SELECT "e" AS cust,
3 AS numb,
"m567" AS val
FROM dual
UNION ALL
SELECT "f" AS cust,
3 AS numb,
"m777" AS val
FROM dual
UNION ALL
SELECT "g" AS cust,
3 AS numb,
"m888" AS val
FROM dual
UNION ALL
SELECT "g" AS cust,
3 AS numb,
"m765" AS val
FROM dual
UNION ALL
SELECT "g" AS cust,
4 AS numb,
"m543" AS val
FROM dual
UNION ALL
SELECT "h" AS cust,
6 AS numb,
"m888" AS val
FROM dual
UNION ALL
SELECT "h" AS cust,
6 AS numb,
"m090" AS val
FROM dual
UNION ALL
SELECT "h" AS cust,
6 AS numb,
"m001" AS val
FROM dual
UNION ALL
SELECT "h" AS cust,
7 AS numb,
"m008" AS val
FROM dual
UNION ALL
SELECT "h" AS cust,
7 AS numb,
"m090" AS val
FROM dual),
helptab2
AS (SELECT /*+materialize */ DISTINCT cust,
numb,
val,
1 AS counter
FROM helptab1
WHERE val = "m765"),
basic (cust, numb, val, counter)
AS (SELECT DISTINCT cust,
numb,
val,
1
FROM helptab2
UNION ALL
SELECT DISTINCT hlp1.cust,
hlp1.numb,
hlp1.val,
counter + 1
FROM basic bas
join helptab1 hlp1
ON ( hlp1.cust = bas.cust
AND hlp1.numb = bas.numb )
OR ( hlp1.val = bas.val )
WHERE counter <= 10),
helptab3
AS (SELECT DISTINCT cust,
numb,
val,
counter
FROM basic
ORDER BY cust,
numb,
val)
SELECT DISTINCT cust,
numb,
val
FROM helptab3;
但是有两个问题,"where counter <= 10" 部分不是很智能,它不能在 Oracle (12g) Developer 上执行。它在 Oracle 上 运行 需要做什么?以及如何处理递归部分,所以没有限制也没有循环?
您查询中的一些问题:
- 文字字符串需要用单引号分隔,而不是双引号。例如:
'm765'
而不是 "m765"
。后者被解释为列引用。
distinct
在递归查询的递归(第二)部分中是不允许的。但是,由于您仍然在下一个 with
查询中应用 distinct
,因此您可以不用它,但会付出巨大的性能代价。注意:在查询的第一部分不需要 distinct
,因为您已经在 helptab2
中这样做了
- 要使
counter <= 10
在递归查询中起作用,它确实需要是一个连接条件,而不是 where
子句中的条件。
应用这些更改后,查询可以运行,但显然变得非常慢,因为递归部分将收集许多相同的记录并递归到循环中。
可以使用递归 with
查询后的 cycle
子句来防止循环:
with helptab1 as (
select 'A' as cust, 1 as numb, 'm006' as val from dual union all
select 'B' as cust, 1 as numb, 'm006' as val from dual union all
select 'B' as cust, 1 as numb, 'm777' as val from dual union all
select 'B' as cust, 2 as numb, 'm018' as val from dual union all
select 'C' as cust, 2 as numb, 'm454' as val from dual union all
select 'C' as cust, 5 as numb, 'm008' as val from dual union all
select 'C' as cust, 5 as numb, 'm090' as val from dual union all
select 'C' as cust, 6 as numb, 'm789' as val from dual union all
select 'C' as cust, 7 as numb, 'm191' as val from dual union all
select 'D' as cust, 9 as numb, 'm006' as val from dual union all
select 'D' as cust, 1 as numb, 'm123' as val from dual union all
select 'E' as cust, 3 as numb, 'm567' as val from dual union all
select 'F' as cust, 3 as numb, 'm777' as val from dual union all
select 'G' as cust, 3 as numb, 'm888' as val from dual union all
select 'G' as cust, 3 as numb, 'm765' as val from dual union all
select 'G' as cust, 4 as numb, 'm543' as val from dual union all
select 'H' as cust, 6 as numb, 'm888' as val from dual union all
select 'H' as cust, 6 as numb, 'm090' as val from dual union all
select 'H' as cust, 6 as numb, 'm001' as val from dual union all
select 'H' as cust, 7 as numb, 'm008' as val from dual union all
select 'H' as cust, 7 as numb, 'm090' as val from dual),
basic (cust,numb,val) as (
select distinct cust, numb, val
from helptab1
where val = 'm765'
union all
select hlp1.cust, hlp1.numb, hlp1.val
from basic bas
join helptab1 hlp1
on hlp1.cust = bas.cust and hlp1.numb = bas.numb
or hlp1.val = bas.val)
cycle cust, numb, val set cycle to 1 default 0
select distinct cust, numb, val
from basic;
这也可以使用分层查询的替代 connect by
语法来完成,它具有 nocycles
选项:
with helptab1 as (
select 'A' as cust, 1 as numb, 'm006' as val from dual union all
select 'B' as cust, 1 as numb, 'm006' as val from dual union all
select 'B' as cust, 1 as numb, 'm777' as val from dual union all
select 'B' as cust, 2 as numb, 'm018' as val from dual union all
select 'C' as cust, 2 as numb, 'm454' as val from dual union all
select 'C' as cust, 5 as numb, 'm008' as val from dual union all
select 'C' as cust, 5 as numb, 'm090' as val from dual union all
select 'C' as cust, 6 as numb, 'm789' as val from dual union all
select 'C' as cust, 7 as numb, 'm191' as val from dual union all
select 'D' as cust, 9 as numb, 'm006' as val from dual union all
select 'D' as cust, 1 as numb, 'm123' as val from dual union all
select 'E' as cust, 3 as numb, 'm567' as val from dual union all
select 'F' as cust, 3 as numb, 'm777' as val from dual union all
select 'G' as cust, 3 as numb, 'm888' as val from dual union all
select 'G' as cust, 3 as numb, 'm765' as val from dual union all
select 'G' as cust, 4 as numb, 'm543' as val from dual union all
select 'H' as cust, 6 as numb, 'm888' as val from dual union all
select 'H' as cust, 6 as numb, 'm090' as val from dual union all
select 'H' as cust, 6 as numb, 'm001' as val from dual union all
select 'H' as cust, 7 as numb, 'm008' as val from dual union all
select 'H' as cust, 7 as numb, 'm090' as val from dual),
basic as (
select cust, numb, val
from helptab1
connect by nocycle
prior cust = cust and prior numb = numb
or prior val = val
start with val = 'm765')
select distinct cust, numb, val
from basic;
以下查询是在 https://www.tutorialspoint.com/execute_sql_online.php 上开发的并且按设计工作:
WITH helptab1
AS (SELECT "a" AS cust,
1 AS numb,
"m006" AS val
FROM dual
UNION ALL
SELECT "b" AS cust,
1 AS numb,
"m006" AS val
FROM dual
UNION ALL
SELECT "b" AS cust,
1 AS numb,
"m777" AS val
FROM dual
UNION ALL
SELECT "b" AS cust,
2 AS numb,
"m018" AS val
FROM dual
UNION ALL
SELECT "c" AS cust,
2 AS numb,
"m454" AS val
FROM dual
UNION ALL
SELECT "c" AS cust,
5 AS numb,
"m008" AS val
FROM dual
UNION ALL
SELECT "c" AS cust,
5 AS numb,
"m090" AS val
FROM dual
UNION ALL
SELECT "c" AS cust,
6 AS numb,
"m789" AS val
FROM dual
UNION ALL
SELECT "c" AS cust,
7 AS numb,
"m191" AS val
FROM dual
UNION ALL
SELECT "d" AS cust,
9 AS numb,
"m006" AS val
FROM dual
UNION ALL
SELECT "d" AS cust,
1 AS numb,
"m123" AS val
FROM dual
UNION ALL
SELECT "e" AS cust,
3 AS numb,
"m567" AS val
FROM dual
UNION ALL
SELECT "f" AS cust,
3 AS numb,
"m777" AS val
FROM dual
UNION ALL
SELECT "g" AS cust,
3 AS numb,
"m888" AS val
FROM dual
UNION ALL
SELECT "g" AS cust,
3 AS numb,
"m765" AS val
FROM dual
UNION ALL
SELECT "g" AS cust,
4 AS numb,
"m543" AS val
FROM dual
UNION ALL
SELECT "h" AS cust,
6 AS numb,
"m888" AS val
FROM dual
UNION ALL
SELECT "h" AS cust,
6 AS numb,
"m090" AS val
FROM dual
UNION ALL
SELECT "h" AS cust,
6 AS numb,
"m001" AS val
FROM dual
UNION ALL
SELECT "h" AS cust,
7 AS numb,
"m008" AS val
FROM dual
UNION ALL
SELECT "h" AS cust,
7 AS numb,
"m090" AS val
FROM dual),
helptab2
AS (SELECT /*+materialize */ DISTINCT cust,
numb,
val,
1 AS counter
FROM helptab1
WHERE val = "m765"),
basic (cust, numb, val, counter)
AS (SELECT DISTINCT cust,
numb,
val,
1
FROM helptab2
UNION ALL
SELECT DISTINCT hlp1.cust,
hlp1.numb,
hlp1.val,
counter + 1
FROM basic bas
join helptab1 hlp1
ON ( hlp1.cust = bas.cust
AND hlp1.numb = bas.numb )
OR ( hlp1.val = bas.val )
WHERE counter <= 10),
helptab3
AS (SELECT DISTINCT cust,
numb,
val,
counter
FROM basic
ORDER BY cust,
numb,
val)
SELECT DISTINCT cust,
numb,
val
FROM helptab3;
但是有两个问题,"where counter <= 10" 部分不是很智能,它不能在 Oracle (12g) Developer 上执行。它在 Oracle 上 运行 需要做什么?以及如何处理递归部分,所以没有限制也没有循环?
您查询中的一些问题:
- 文字字符串需要用单引号分隔,而不是双引号。例如:
'm765'
而不是"m765"
。后者被解释为列引用。 distinct
在递归查询的递归(第二)部分中是不允许的。但是,由于您仍然在下一个with
查询中应用distinct
,因此您可以不用它,但会付出巨大的性能代价。注意:在查询的第一部分不需要distinct
,因为您已经在helptab2
中这样做了
- 要使
counter <= 10
在递归查询中起作用,它确实需要是一个连接条件,而不是where
子句中的条件。
应用这些更改后,查询可以运行,但显然变得非常慢,因为递归部分将收集许多相同的记录并递归到循环中。
可以使用递归 with
查询后的 cycle
子句来防止循环:
with helptab1 as (
select 'A' as cust, 1 as numb, 'm006' as val from dual union all
select 'B' as cust, 1 as numb, 'm006' as val from dual union all
select 'B' as cust, 1 as numb, 'm777' as val from dual union all
select 'B' as cust, 2 as numb, 'm018' as val from dual union all
select 'C' as cust, 2 as numb, 'm454' as val from dual union all
select 'C' as cust, 5 as numb, 'm008' as val from dual union all
select 'C' as cust, 5 as numb, 'm090' as val from dual union all
select 'C' as cust, 6 as numb, 'm789' as val from dual union all
select 'C' as cust, 7 as numb, 'm191' as val from dual union all
select 'D' as cust, 9 as numb, 'm006' as val from dual union all
select 'D' as cust, 1 as numb, 'm123' as val from dual union all
select 'E' as cust, 3 as numb, 'm567' as val from dual union all
select 'F' as cust, 3 as numb, 'm777' as val from dual union all
select 'G' as cust, 3 as numb, 'm888' as val from dual union all
select 'G' as cust, 3 as numb, 'm765' as val from dual union all
select 'G' as cust, 4 as numb, 'm543' as val from dual union all
select 'H' as cust, 6 as numb, 'm888' as val from dual union all
select 'H' as cust, 6 as numb, 'm090' as val from dual union all
select 'H' as cust, 6 as numb, 'm001' as val from dual union all
select 'H' as cust, 7 as numb, 'm008' as val from dual union all
select 'H' as cust, 7 as numb, 'm090' as val from dual),
basic (cust,numb,val) as (
select distinct cust, numb, val
from helptab1
where val = 'm765'
union all
select hlp1.cust, hlp1.numb, hlp1.val
from basic bas
join helptab1 hlp1
on hlp1.cust = bas.cust and hlp1.numb = bas.numb
or hlp1.val = bas.val)
cycle cust, numb, val set cycle to 1 default 0
select distinct cust, numb, val
from basic;
这也可以使用分层查询的替代 connect by
语法来完成,它具有 nocycles
选项:
with helptab1 as (
select 'A' as cust, 1 as numb, 'm006' as val from dual union all
select 'B' as cust, 1 as numb, 'm006' as val from dual union all
select 'B' as cust, 1 as numb, 'm777' as val from dual union all
select 'B' as cust, 2 as numb, 'm018' as val from dual union all
select 'C' as cust, 2 as numb, 'm454' as val from dual union all
select 'C' as cust, 5 as numb, 'm008' as val from dual union all
select 'C' as cust, 5 as numb, 'm090' as val from dual union all
select 'C' as cust, 6 as numb, 'm789' as val from dual union all
select 'C' as cust, 7 as numb, 'm191' as val from dual union all
select 'D' as cust, 9 as numb, 'm006' as val from dual union all
select 'D' as cust, 1 as numb, 'm123' as val from dual union all
select 'E' as cust, 3 as numb, 'm567' as val from dual union all
select 'F' as cust, 3 as numb, 'm777' as val from dual union all
select 'G' as cust, 3 as numb, 'm888' as val from dual union all
select 'G' as cust, 3 as numb, 'm765' as val from dual union all
select 'G' as cust, 4 as numb, 'm543' as val from dual union all
select 'H' as cust, 6 as numb, 'm888' as val from dual union all
select 'H' as cust, 6 as numb, 'm090' as val from dual union all
select 'H' as cust, 6 as numb, 'm001' as val from dual union all
select 'H' as cust, 7 as numb, 'm008' as val from dual union all
select 'H' as cust, 7 as numb, 'm090' as val from dual),
basic as (
select cust, numb, val
from helptab1
connect by nocycle
prior cust = cust and prior numb = numb
or prior val = val
start with val = 'm765')
select distinct cust, numb, val
from basic;