先通过连接会产生意外结果

时间:2016-03-15 12:27:00

标签: sql oracle

这个select语句会产生奇怪的结果:

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;

我预计connect by仅针对id = 1的行完成,但结果为:

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

即。首先,对所有行执行connect by,然后按id过滤结果。

作为解决方法,以下语句提供了正确的结果:

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

但我想应该有更简单的方法来实现这一目标?提前谢谢!

2 个答案:

答案 0 :(得分:2)

使用CONNECT BY限制与同一ID的连接:

CONNECT BY PRIOR id = id
       AND PRIOR seqno = cs_id_prev_seqno

哪会使您的查询:

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;
  

我预计connect by仅针对id = 1

的行完成

正如你所知,这不是真的。查询将找到以下所有行:

  • 根行有cs_prev_seqno = 0(根据START WITH子句);
  • 当前行有id = 1WHERE子句);和
  • 那个,在每一步,从根到当前行,CONNECT BY子句得到满足。

如果CONNECT BY子句未指定ID应该是常量,则不会检查这些干预步骤。

答案 1 :(得分:1)

如果我理解得很清楚,您可以在应用CONNECT BY之前尝试限制数据集,例如:

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;

您甚至可以在CONNECT BY子句中使用您的条件:

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;