2010-10-20 38 views
3

好的,我有一個複雜的遞歸問題。我想在我的Oracle 11g數據庫中獲得所有對象(all_objects表)的依賴性安裝序列。獲取Oracle對象的安裝順序

首先,我創建了使用此函數保持

我已經嘗試做concate所有依賴的所有依賴

create or replace 
view REALLY_ALL_DEPENDENCIES as 
select * 
    from ALL_DEPENDENCIES 
union 
select owner, index_name, 'INDEX', table_owner, table_name, table_type, null, null 
    from all_indexes 
union 
select p.owner, p.table_name, 'TABLE', f.owner, f.table_name, 'TABLE', null, null 
    from all_constraints p 
    join all_constraints f 
    on F.R_CONSTRAINT_NAME = P.CONSTRAINT_NAME 
    and F.CONSTRAINT_TYPE = 'R' 
    and p.constraint_type='P' 
; 
/

編輯觀點:

create 
or replace 
function dependency(
    i_name varchar2 
    ,i_type varchar2 
    ,i_owner varchar2 
    ,i_level number := 0 
    ,i_token clob := ' ') return clob 
is 
    l_token clob := i_token; 
    l_exist number := 0; 
begin 
    select count(*) into l_exist 
    from all_objects 
    where object_name = i_name 
     and object_type = i_type 
     and owner = i_owner; 

    if l_exist > 0 then 
    l_token := l_token || ';' || i_level || ';' || 
     i_name || ':' || i_type || ':' || i_owner; 
    else 
    -- if not exist function recursion is finished 
    return l_token; 
    end if; 

    for tupl in (
    select distinct 
     referenced_name 
     ,referenced_type 
     ,referenced_owner 
     from REALLY_ALL_DEPENDENCIES 
     where name = i_name 
     and type = i_type 
     and owner = i_owner 
    ) 
    loop 
    -- if cyclic dependency stop and shout! 
    if i_token like '%' || tupl.referenced_name || ':' || tupl.referenced_type || ':' || tupl.referenced_owner || '%' then 
     select count(*) into l_exist 
     from REALLY_ALL_DEPENDENCIES 
     where name = tupl.referenced_name 
      and type = tupl.referenced_type 
      and owner = tupl.referenced_owner; 
     if l_exist > 0 then 
     return '!!!CYCLIC!!! (' || i_level || ';' || tupl.referenced_name || ':' || tupl.referenced_type || ':' || tupl.referenced_owner || '):' || l_token; 
     end if; 
    end if; 

    -- go into recursion 
    l_token := dependency(
     tupl.referenced_name 
     ,tupl.referenced_type 
     ,i_owner /* I just want my own sources */ 
     ,i_level +1 
     ,l_token); 
    end loop; 

    -- no cyclic condition and loop is finished 
    return l_token; 
end; 
/

而且我可以查詢通過

select 
    object_name 
    ,object_type 
    ,owner 
    ,to_char(dependency(object_name, object_type, owner)) as dependecy 
    from all_objects 
    where owner = 'SYSTEM' 
; 

好的,也許它就像「作弊」,但你不能在創建時做循環依賴。所以至少作爲一個人,我只能創建一個接一個的對象:-)這個序列應該是「逆向工程能力」。

現在我對解決方案比以前更感興趣;-)它仍然是關於棘手的部分......「我怎樣才能從架構順序安裝序列中選擇所有的元素(使用之前的依賴對象列表目的)」? 這只是某種排序問題,不是嗎?

+0

既然你可以有循環依賴,這是不可能的。 – 2010-10-21 18:06:39

+0

是的,但正如你所看到的,我能夠檢測到它們。因此,然後讓我們添加一個約束 - >通過依賴關係排序所有源,並將循環源排序到結果集中的最後。現在有可能得到一個解決方案;-)好吧,mybe你不想這樣做,因爲它對你沒有意義,但它仍然對我有意義:-) – christian 2010-10-22 08:12:42

回答

3

通常你通過按特定順序創建對象來「作弊」。例如,您可能首先創建序列(它們具有零依賴關係)。然後你可以做桌子。之後,包裝規格,然後包裝體,等等。

請記住,軟件包之間可能存在循環依賴關係,因此在創建時無法滿足所有依賴關係。

這裏的商業案例是什麼?有真正的「問題」還是隻是一個練習?

編輯

我們使用出口對象按以下順序導出工具:

  • 數據庫鏈接
  • 序列
  • 類型
  • 查看
  • 個主鍵
  • 指標
  • 外鍵
  • 約束
  • 觸發器
  • 物化視圖
  • 物化視圖日誌
  • 包裝規格
  • 包主體
  • 程序
  • 功能

最後,我們運行dbms_utility.compile_schema過程以確保所有內容都有效,並且不會遺漏任何依賴關係。如果您使用的其他對象類型不是這些,我不確定他們在這個序列中的位置。

+3

有一件事是要創建視圖與FORCE選項,所以即使缺少函數依賴關係,視圖定義也會被保存。 – 2010-10-20 16:04:30

+0

我們的某些視圖調用了打包函數,因此我們首先編譯包規範,然後查看並打包主體 – Rene 2010-10-21 06:00:27

+0

我會說這兩種:-)事實上,我目前在一個開源項目中工作,我們在oracle中做了很多工作。問題在於何時有人損壞了他的數據庫並想從全新安裝開始。然後你必須從svn下載一大堆(> 100)sql源代碼。手動安裝它們並不舒服,所以我想寫一些install.sql,人們不必關心保險順序。也許有更好的方法,然後我想 - >獲得依賴? – christian 2010-10-21 06:28:36

0

好吧,我有一段時間再次看這份工作,我想分享結果。也許另一個人遇到這個線程尋找解決方案。首先,我將SQL作爲SYS執行,但我認爲您可以在每個使用公共同義詞的模式中執行此操作。

過程「exec obj_install_seq.make_install('SCOTT');」假設你的源文件被稱爲「object_name.object_type.sql」,則會生成一個包含sql +兼容sql文件的clob。只需將其緩存即可。

乾杯 克里斯

create global temporary table DEPENDENCIES on commit delete rows as 
select * from ALL_DEPENDENCIES where 1=2 ; 
/

create global temporary table install_seq(
    idx number 
    ,seq number 
    ,iter number 
    ,owner varchar2(30) 
    ,name varchar2(30) 
    ,type varchar2(30) 
) on commit delete rows; 
/

create global temporary table loop_chk(
    iter number 
    ,lvl number 
    ,owner varchar2(30) 
    ,name varchar2(30) 
    ,type varchar2(30) 
) on commit delete rows; 
/

create or replace package obj_install_seq is 
    procedure make_install(i_schema varchar2 := 'SYSTEM'); 
end; 
/

create or replace package body obj_install_seq is 
    subtype install_seq_t is install_seq%rowtype; 
    type dependency_list_t is table of DEPENDENCIES%rowtype; 

    procedure set_list_data(i_schema varchar2 := user) 
    is 
    l_owner varchar2(30) := i_schema; 
    begin 
    -- collect all dependencies 
    insert into DEPENDENCIES 
     select * 
     from (select * 
       from ALL_DEPENDENCIES 
      where owner = l_owner 
       and referenced_owner = l_owner 
      union 
      select owner, index_name, 'INDEX', table_owner, table_name, table_type, null, null 
       from all_indexes 
      where owner = l_owner 
       and table_owner = l_owner 
      union 
      select p.owner, p.table_name, 'TABLE', f.owner, f.table_name, 'TABLE', null, null 
       from all_constraints p 
       join all_constraints f 
       on F.R_CONSTRAINT_NAME = P.CONSTRAINT_NAME 
       and F.CONSTRAINT_TYPE = 'R' 
       and p.constraint_type='P' 
       and p.owner = f.owner 
      where p.owner = l_owner 
      ) all_dep_tab; 

    -- collect all objects 
    insert into install_seq 
    select rownum, null,null, owner, object_name, object_type 
     from (select distinct owner, object_name, object_type, created 
       from all_objects 
       where owner = l_owner 
       order by created) objs; 
    end; 

    function is_referencing(
     i_owner varchar2 
    ,i_name varchar2 
    ,i_type varchar2 
    ,i_iter number 
    ,i_level number := 0 
) return boolean 
    is 
    l_cnt number; 
    begin 
    select count(*) into l_cnt 
     from loop_chk 
    where name = i_name 
     and owner = i_owner 
     and type = i_type 
     and iter = i_iter 
     and lvl < i_level; 

    insert into loop_chk values(i_iter,i_level,i_owner,i_name,i_type); 

    if l_cnt > 0 then 
     return true; 
    else 
     return false; 
    end if; 
    end; 

    procedure set_seq(
    i_owner varchar2 
    ,i_name varchar2 
    ,i_type varchar2 
    ,i_iter number 
    ,i_level number := 0) 
    is 
    -- l_dep all_dependencies%rowtype; 
    l_idx number; 
    l_level number := i_level +1; 
    l_dep_list dependency_list_t; 
    l_cnt number; 
    begin 
    -- check for dependend source 
    begin 
     select * bulk collect into l_dep_list 
     from dependencies 
     where name = i_name 
     and owner = i_owner 
     and type = i_type; 

     if l_dep_list.count <= 0 then 
     -- recursion finished 
     return; 
     end if; 
    end; 

    for i in 1..l_dep_list.count loop 
     if is_referencing( 
     l_dep_list(i).referenced_owner 
     ,l_dep_list(i).referenced_name 
     ,l_dep_list(i).referenced_type 
     ,i_iter 
     ,i_level 
    ) then 
     -- cyclic dependecy 
     update install_seq 
      set seq = 999 
       ,iter = i_iter 
     where name = l_dep_list(i).referenced_name 
      and owner = l_dep_list(i).referenced_owner 
      and type = l_dep_list(i).referenced_type; 
     else 
     --chek if sequence is earlier 
     select count(*) into l_cnt 
      from install_seq 
     where name = l_dep_list(i).referenced_name 
      and owner = l_dep_list(i).referenced_owner 
      and type = l_dep_list(i).referenced_type 
      and seq > l_level *-1; 

     -- set sequence  
     if l_cnt > 0 then 
      update install_seq 
      set seq = l_level *-1 
       ,iter = i_iter 
      where name = l_dep_list(i).referenced_name 
      and owner = l_dep_list(i).referenced_owner 
      and type = l_dep_list(i).referenced_type; 
     end if; 

     -- go recusrion 
     set_seq(
      l_dep_list(i).referenced_owner 
      ,l_dep_list(i).referenced_name 
      ,l_dep_list(i).referenced_type 
      ,i_iter + (i-1) 
      ,l_level 
    ); 

     end if;   
    end loop; 
    end; 


    function get_next_idx return number 
    is 
    l_idx number; 
    begin 
    select min(idx) into l_idx 
     from install_seq 
    where seq is null; 

    return l_idx; 
    end; 

    procedure make_install(i_schema varchar2 := 'SYSTEM') 
    is 
    l_obj install_seq_t; 
    l_idx number; 
    l_iter number := 0; 
    l_install_clob clob := chr(10); 
    begin 
    set_list_data(i_schema); 
    l_idx := get_next_idx; 

    while l_idx is not null loop 
     l_iter := l_iter +1; 

     select * into l_obj from install_seq where idx = l_idx; 
     update install_seq set iter = l_iter where idx = l_idx; 
     update install_seq set seq = 0 where idx = l_idx; 
     set_seq(l_obj.owner,l_obj.name,l_obj.type,l_iter); 

     l_idx := get_next_idx; 
    end loop; 

    for tupl in (select * from install_seq order by seq, iter, idx) loop 
     l_install_clob := l_install_clob || '@' || 
     replace(tupl.name,' ' ,'') || '.' || 
     replace(tupl.type,' ' ,'') || '.sql' || 
     chr(10); 
    end loop; 

    l_install_clob := l_install_clob || 
     'exec dbms_utility.compile_schema(''' || upper(i_schema) || ''');'; 

    -- do with the install file what you want 
    DBMS_OUTPUT.PUT_LINE(dbms_lob.substr(l_install_clob,4000)); 
    end; 
end; 
/