Oracle的Connect By理解


connect by中的條件就表示了父子之間的連接關系 比如 connect by id=prior pid,但如果connect by中的條件沒有表示記錄之間的父子關系那會出現什么情況?

常見的,connect by會在構造序列的時候使用
select rownum from dual connect by rownum<xxx
代替早期版本的
select rownum from all_objects where rownum <xxx

我們注意到,dual是一個只有一條記錄的表,如果表有多條記錄,將會怎樣?

下面開始實驗

CREATE TABLE T(ID VARCHAR2(1 BYTE));

INSERT INTO T ( ID ) VALUES ( 'A'); 
INSERT INTO T ( ID ) VALUES ( 'B'); 
INSERT INTO T ( ID ) VALUES ( 'C'); 
COMMIT;

SQL> select id,level from t connect by level<2;
I LEVEL
- ----------
A 1
B 1
C 1
SQL> select id,level from t connect by level<3;
I LEVEL
- ----------
A 1
A 2
B 2
C 2
B 1
A 2
B 2
C 2
C 1
A 2
B 2
C 2
已選擇12行。
SQL> select id,level from t connect by level<4;
I LEVEL
- ----------
A 1
A 2
A 3
B 3
C 3
B 2
A 3
B 3
C 3
C 2
A 3
B 3
C 3
B 1
A 2
A 3
B 3
C 3
B 2
A 3
B 3
C 3
C 2
A 3
B 3
C 3
C 1
A 2
A 3
B 3
C 3
B 2
A 3
B 3
C 3
C 2
A 3
B 3
C 3
已選擇39行。

我們很快可以找到其中的規律,假設表中有N條記錄, 則記F(N,l)為select id,level from t connect by level<l 的結果集數目
那么,
F(N,1)=N
F(N,l) = F(N,l-1)*N+N

於是可以總結出
F(N,l)=∑power(N,p), p取值為[1,l)

要解釋,也很容易:當連接條件不能限制記錄之間的關系時每一條記錄都可以作為自己或者其他記錄的葉子
如下所示:
A          1
A          2
A          3
B          3
C          3
B          2
A          3
B          3
C          3
C          2
A          3
B          3
C          3

在這里,我們看到的是Oracle采用了深度優先的算法

我們接着看一個例子,看看在SQL中通過connect by如何將任意一個整數(不要太大就行)拆分為若干個power(2,n)的和的方法。
先構造測試數據:

create table ba(n number);

insert into ba select 5*rownum from dual connect by rownum<5;

commit;

select * from ba;
N
-------
5
10
15
20

一個得出結果的簡單的SQL為

  1. select distinct a.n , level, bitand(a.n,power(2,level-1)) from ba a connect by level<=floor(log(2,n)+1)

這里為什么要加distinct?你可以嘗試去掉distinct ,看看結果與保持distinct有多大差別。

然后我們先來看,如果只對其中的一條記錄進行操作,那么加不加distinct,結果是否是一樣的?比如我們只看第一條記錄5的拆分結果

select distinct a.n , level, bitand(a.n,power(2,level-1)) from (select * from ba where rownum=1) a connect by level<=floor(log(2,n)+1);

結果為:

  1. N    LEVEL    BITAND(A.N,POWER(2,LEVEL-1))
  2. ----------------------------------------------------------------
  3. 5    1             1
  4. 5    2             0
  5. 5    3             4

去掉distinct的sql為

  1. select a.n , level, bitand(a.n,power(2,level-1)) from (select * from ba where rownum=1) a connect by level<=floor(log(2,n)+1);

輸出結果,自己運行一下看看。然后你就該思考了,為什么你看到的結果會是這樣???

這里不做過多解釋,做完上面的實驗,然后結合1樓中所說的,我想你應該就能明白了。

事實上我們有更好的辦法來處理:

with a as (select n, floor(log(2,n)+1) lc from ba)

select a.n, bitand(a.n,power(2,b.rn-1)) from a, 

(select rownum rn from

        (select max(lc) mlc from a) 

        connect by level<=mlc

)b

where rn<=a.lc

order by 1,2

內層SQL先取得所有記錄中可拆分出來的power(2,n)中的n最大可能是多少,然后由此構造出序列,最后再做一次關聯查詢,

用限制條件rn<=a.lc限制住每個N中可拆分出來的power(2,n)中的n的最大值,由此可以高效得出結果。

上例實質上與 對多記錄按各自指定次數重復 的性質是一樣的。

簡單總結:
對單記錄/單條數據使用connect by,沒問題
但對多條記錄使用connect by,就會碰到問題,千萬要注意。

elect rownum,
       level,
       sys_connect_by_path(id, ',') path,
       id,
       connect_by_isleaf isleaf
  from t
connect by nocycle level < 2
order by rownum, level, path;

select rownum,
       level,
       sys_connect_by_path(id, ',') path,
       id,
       connect_by_isleaf isleaf
  from t
connect by nocycle level < 3
order by rownum, level, path;

select rownum,
       level,
       sys_connect_by_path(id, ',') path,
       id,
       connect_by_isleaf isleaf
  from t
connect by nocycle level < 4
order by rownum, level, path;

下面是執行結果:
CHENCH@orcl> select rownum,
  2         level,
  3         sys_connect_by_path(id, ',') path,
  4         id,
  5         connect_by_isleaf isleaf
  6    from t
  7  connect by nocycle level < 2
  8   order by rownum, level, path;

    ROWNUM      LEVEL PATH       ID     ISLEAF
---------- ---------- ---------- -- ----------
         1          1 ,A         A           1
         2          1 ,B         B           1
         3          1 ,C         C           1

Elapsed: 00:00:00.01
CHENCH@orcl>
CHENCH@orcl> select rownum,
  2         level,
  3         sys_connect_by_path(id, ',') path,
  4         id,
  5         connect_by_isleaf isleaf
  6    from t
  7  connect by nocycle level < 3
  8   order by rownum, level, path;

    ROWNUM      LEVEL PATH       ID     ISLEAF
---------- ---------- ---------- -- ----------
         1          1 ,A         A           0
         2          2 ,A,A       A           1
         3          2 ,A,B       B           1
         4          2 ,A,C       C           1
         5          1 ,B         B           0
         6          2 ,B,A       A           1
         7          2 ,B,B       B           1
         8          2 ,B,C       C           1
         9          1 ,C         C           0
        10          2 ,C,A       A           1
        11          2 ,C,B       B           1

    ROWNUM      LEVEL PATH       ID     ISLEAF
---------- ---------- ---------- -- ----------
        12          2 ,C,C       C           1

12 rows selected.

Elapsed: 00:00:00.01
CHENCH@orcl>
CHENCH@orcl> select rownum,
  2         level,
  3         sys_connect_by_path(id, ',') path,
  4         id,
  5         connect_by_isleaf isleaf
  6    from t
  7  connect by nocycle level < 4
  8   order by rownum, level, path;

    ROWNUM      LEVEL PATH       ID     ISLEAF
---------- ---------- ---------- -- ----------
         1          1 ,A         A           0
         2          2 ,A,A       A           0
         3          3 ,A,A,A     A           1
         4          3 ,A,A,B     B           1
         5          3 ,A,A,C     C           1
         6          2 ,A,B       B           0
         7          3 ,A,B,A     A           1
         8          3 ,A,B,B     B           1
         9          3 ,A,B,C     C           1
        10          2 ,A,C       C           0
        11          3 ,A,C,A     A           1

    ROWNUM      LEVEL PATH       ID     ISLEAF
---------- ---------- ---------- -- ----------
        12          3 ,A,C,B     B           1
        13          3 ,A,C,C     C           1
        14          1 ,B         B           0
        15          2 ,B,A       A           0
        16          3 ,B,A,A     A           1
        17          3 ,B,A,B     B           1
        18          3 ,B,A,C     C           1
        19          2 ,B,B       B           0
        20          3 ,B,B,A     A           1
        21          3 ,B,B,B     B           1
        22          3 ,B,B,C     C           1

    ROWNUM      LEVEL PATH       ID     ISLEAF
---------- ---------- ---------- -- ----------
        23          2 ,B,C       C           0
        24          3 ,B,C,A     A           1
        25          3 ,B,C,B     B           1
        26          3 ,B,C,C     C           1
        27          1 ,C         C           0
        28          2 ,C,A       A           0
        29          3 ,C,A,A     A           1
        30          3 ,C,A,B     B           1
        31          3 ,C,A,C     C           1
        32          2 ,C,B       B           0
        33          3 ,C,B,A     A           1

    ROWNUM      LEVEL PATH       ID     ISLEAF
---------- ---------- ---------- -- ----------
        34          3 ,C,B,B     B           1
        35          3 ,C,B,C     C           1
        36          2 ,C,C       C           0
        37          3 ,C,C,A     A           1
        38          3 ,C,C,B     B           1
        39          3 ,C,C,C     C           1

39 rows selected.

Elapsed: 00:00:00.04
關鍵是 connect by 后面不管是level 還是 rownum都是在查詢結果集內作的限制

SQL> with t as ( 2 select 1 from dual 3 union all 4 select 2 from dual 5 union all 6 select 3 from dual) 7 select * from t connect by rownum < 5; 1 ---------- 1 1 1 1 2 3 已選擇6行。 SQL> with t as (select 1 from dual) 2 select * from t connect by rownum < 5; 1 ---------- 1 1 1 1 已選擇4行。
with a as (
    select 5 as n from dual
    union all
    select 10 from dual
    union all
    select 15 from dual
    union all
    select 20 from dual
    )
select  distinct a.n , level, bitand(a.n,power(2,level-1)) from a connect by level<=floor(log(2,n)+1);

SQL> with a as (
  2      select 5 as n from dual
  3      union all
  4      select 10 from dual
  5      union all
  6      select 15 from dual
  7      union all
  8      select 20 from dual
  9     )
 10  select  distinct a.n , level, bitand(a.n,power(2,level-1)) from a connect by level<=floor(log(2,n)+1);

         N      LEVEL BITAND(A.N,POWER(2,LEVEL-1))
---------- ---------- ----------------------------
        10          4                            8
        15          2                            2
        20          2                            0
        20          4                            0
        15          3                            4
         5          1                            1
        10          3                            0
        10          1                            0
        20          3                            4
         5          2                            0
        20          5                           16
        10          2                            2
         5          3                            4
        15          4                            8
        15          1                            1
        20          1                            0

已選擇16行。
SQL> with a as (
  2          select 5 as n from dual
  3          union all
  4          select 10 from dual
  5          union all
  6          select 15 from dual
  7          union all
  8          select 20 from dual
  9         )
 10     select  distinct a.n , level, bitand(a.n,power(2,level-1)) from (select * from a where rownum=1) a connect by level<=floor(log(2,n)+1);

         N      LEVEL BITAND(A.N,POWER(2,LEVEL-1))
---------- ---------- ----------------------------
         5          1                            1
         5          2                            0
         5          3                            4

已選擇3行。


SQL> with a as (
  2          select 5 as n from dual
  3          union all
  4          select 10 from dual
  5          union all
  6          select 15 from dual
  7          union all
  8          select 20 from dual
  9         )
 10     select  a.n , level, bitand(a.n,power(2,level-1)) from (select * from a where rownum=1) a connect by level<=floor(log(2,n)+1);

         N      LEVEL BITAND(A.N,POWER(2,LEVEL-1))
---------- ---------- ----------------------------
         5          1                            1
         5          2                            0
         5          3                            4

已選擇3行。

 

 

SQL>    with a as (
  2          select 5 as n from dual
  3          union all
  4          select 10 from dual
  5          union all
  6          select 15 from dual
  7          union all
  8          select 20 from dual
  9         )
 10     select * from a where a.n=5 connect by rownum<6;

         N
----------
         5
         5
         5
         5
         5
SQL>    with a as (
  2          select 5 as n from dual
  3          union all
  4          select 10 from dual
  5          union all
  6          select 15 from dual
  7          union all
  8          select 20 from dual
  9         )
 10     select * from a where rownum=1 connect by rownum<6;
ERROR:
ORA-30009: CONNECT BY 操作內存不足
 with a as (
      select 5 as n from dual
      union all
      select 10 from dual
      union all
      select 15 from dual
      union all
      select 20 from dual
     )
 select * from a where a.n=5 connect by level<6;
5
5
5
5
5
5
5
5
5
5

已選擇341行。

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM