Reputation: 2043
This select statement delivers strange results:
with data(id, seqno, cs_prev_seqno, descr) as
(select 1, 1, 0, 'Id 1 Step1' from dual
union all
select 1, 2, 1, 'Id 1 Step2' from dual
union all
select 1, 3, 2, 'Id 1 Step3' from dual
union all
select 1, 4, 1, 'Id 1 Step4' from dual
union all
select 2, 1, 0, 'Id 2 Step1' from dual
union all
select 2, 2, 1, 'Id 2 Step2' from dual)
select id,
sys_connect_by_path(seqno, '/') as path,
seqno,
cs_prev_seqno,
descr,
level
from data
where id = 1
connect by prior seqno = cs_prev_seqno
start with cs_prev_seqno = 0;
I expected that the connect by
is only done for rows with id=1, but the results are:
id path seq seq_prev descr level
1 /1 1 0 Id 1 Step1 1
1 /1/2 2 1 Id 1 Step2 2
1 /1/2/3 3 2 Id 1 Step3 3
1 /1/2/3 3 2 Id 1 Step3 3
1 /1/4 4 1 Id 1 Step4 2
1 /1/2 2 1 Id 1 Step2 2
1 /1/2/3 3 2 Id 1 Step3 3
1 /1/2/3 3 2 Id 1 Step3 3
1 /1/4 4 1 Id 1 Step4 2
I.E. first of all the connect by
is done for all rows, afterwards the result is filtered by id.
As a workaround, the following statement delivers the correct results:
with data(id,
seqno,
cs_prev_seqno,
descr) as
(select 1, 1, 0, 'Id 1 Step1'
from dual
union all
select 1, 2, 1, 'Id 1 Step2'
from dual
union all
select 1, 3, 2, 'Id 1 Step3'
from dual
union all
select 1, 4, 1, 'Id 1 Step4'
from dual
union all
select 2, 1, 0, 'Id 2 Step1'
from dual
union all
select 2, 2, 1, 'Id 2 Step2'
from dual
)
,
data2 as
(select d.id,
d.seqno,
d.cs_prev_seqno,
d.id || '.' || d.seqno as id_seqno,
d.id || '.' || d.cs_prev_seqno as cs_id_prev_seqno,
d.descr
from data d)
select id,
sys_connect_by_path(seqno, '/') as path,
seqno,
cs_prev_seqno,
descr,
level
from data2
where id = 1
connect by prior id_seqno = cs_id_prev_seqno
start with cs_prev_seqno = 0;
-->
id path seq seq_prev descr level
1 /1 1 0 Id 1 Step1 1
1 /1/2 2 1 Id 1 Step2 2
1 /1/2/3 3 2 Id 1 Step3 3
1 /1/4 4 1 Id 1 Step4 2
But I guess there should be an easier way to achieve this? Thanks in advance!
Upvotes: 2
Views: 38
Reputation: 168623
Use CONNECT BY
to restrict connections to the same ID
:
CONNECT BY PRIOR id = id
AND PRIOR seqno = cs_id_prev_seqno
Which would make your query:
SELECT id,
SYS_CONNECT_BY_PATH(seqno, '/') AS path,
seqno,
cs_prev_seqno,
descr,
level
FROM data d
WHERE id = 1
CONNECT BY PRIOR id = id
AND PRIOR seqno = cs_id_prev_seqno
START WITH cs_prev_seqno = 0;
I expected that the
connect by
is only done for rows with id=1
As you found out, this is not true. The query will find all the rows where:
cs_prev_seqno = 0
(as per the START WITH
clause);id = 1
(the WHERE
clause); andCONNECT BY
clause is satisfied.If the CONNECT BY
clause does not specify that the ID
should be constant then it will not be checked on those intervening steps.
Upvotes: 2
Reputation: 22969
If I understand well, you can try limiting dataset before applying the CONNECT BY
, for example:
with data(id, seqno, cs_prev_seqno, descr) as
(select 1, 1, 0, 'Id 1 Step1' from dual union all
select 1, 2, 1, 'Id 1 Step2' from dual union all
select 1, 3, 2, 'Id 1 Step3' from dual union all
select 1, 4, 1, 'Id 1 Step4' from dual union all
select 2, 1, 0, 'Id 2 Step1' from dual union all
select 2, 2, 1, 'Id 2 Step2' from dual)
select id,
sys_connect_by_path(seqno, '/') as path,
seqno,
cs_prev_seqno,
descr,
level
from ( select * from data where id = 1)
connect by prior seqno = cs_prev_seqno
start with cs_prev_seqno = 0;
You could even use your condition in the CONNECT BY
clause:
with data(id, seqno, cs_prev_seqno, descr) as
(select 1, 1, 0, 'Id 1 Step1' from dual union all
select 1, 2, 1, 'Id 1 Step2' from dual union all
select 1, 3, 2, 'Id 1 Step3' from dual union all
select 1, 4, 1, 'Id 1 Step4' from dual union all
select 2, 1, 0, 'Id 2 Step1' from dual union all
select 2, 2, 1, 'Id 2 Step2' from dual)
select id,
sys_connect_by_path(seqno, '/') as path,
seqno,
cs_prev_seqno,
descr,
level
from data where id = 1
connect by prior seqno = cs_prev_seqno
and prior id = 1
start with cs_prev_seqno = 0;
Upvotes: 1