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 <xxxsql

咱們注意到,dual是一個只有一條記錄的表,若是表有多條記錄,將會怎樣?測試

下面開始實驗spa

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+Ncode

因而能夠總結出
F(N,l)=∑power(N,p), p取值爲[1,l)blog

要解釋,也很容易:當鏈接條件不能限制記錄之間的關係時每一條記錄均可以做爲本身或者其餘記錄的葉子
以下所示:
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)的和的方法。
先構造測試數據:it

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爲io

  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的拆分結果table

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行。
相關文章
相關標籤/搜索