2010-10-20 2 views
3

Хорошо, у меня есть сложная проблема рекурсии. Я хочу получить последовательность установки dependecy всех моих объектов (all_objects table) в моей базе данных Oracle 11g.Получить последовательность установки объектов Oracle

Сначала я создал представление, проведение всех зависимостей

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' 
; 
/

EDIT

Я пытался сделать concate все зависимости с помощью этой функции:

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' 
; 

Хорошо, может быть, это что-то вроде «обмана», но вы не можете делать циклические зависимости во время создания. Так что, по крайней мере, как человек, я могу только создать один объект за другим :-) И эта последовательность должна быть «обратная инженерия».

Теперь меня больше интересует решение, чем раньше ;-) И это все еще касается сложной части ... «Как я могу выбрать все источники из схемы orderd своей последовательностью установки (список зависимых объектов перед использованием объект) "? Это просто какая-то проблема сортировки, не так ли?

+0

Поскольку вы можете иметь циклических зависимостей, может оказаться невозможным. –

+0

Да, но, как вы можете видеть, я могу их обнаружить. Итак, затем добавьте contraint -> сортировать все источники по зависимостям и сортировать циклические источники до конца в наборе результатов. Теперь можно получить решение ;-) Хорошо, мибе, ты не хочешь это делать, потому что это не имеет для тебя смысла, но мне все же имеет смысл :-) – christian

ответ

3

Обычно вы «обманываете», создавая объекты в определенном порядке. Например, сначала вы можете создавать последовательности (они имеют нулевые зависимости). Тогда вы можете делать таблицы. После этого, спецификации пакета, затем тела пакета и так далее.

Имейте в виду, что возможно иметь циклические зависимости между пакетами, поэтому бывают случаи, когда все равно невозможно удовлетворить все зависимости при создании.

В чем дело? Есть ли настоящая «проблема» или просто упражнение?

EDIT

Инструмент экспорта мы используем экспорт объектов в следующем порядке:

  • База данных Ссылка
  • последовательностей
  • Типов
  • Столы
  • Просмотров
  • Первичных ключи
  • Индексы
  • Внешних ключи
  • Ограничение
  • Триггеры
  • материализованных представления
  • Материализованных Просмотр журналы
  • функции пакета
  • тело пакета
  • Процедура
  • Функции

В конце мы запускаем процедуру dbms_utility.compile_schema, чтобы убедиться, что все допустимо, и никакие зависимости не пропущены. Если вы используете другие типы объектов, чем эти, я не уверен, где они будут проходить в этой последовательности.

+3

Одна вещь, которую нужно сделать - создать виды с помощью FORCE, поэтому определение вида сохраняется, даже если отсутствует функциональная зависимость. –

+0

Некоторые из наших представлений называют упакованные функции, поэтому мы сначала компилируем спецификации пакетов, затем виды, а затем купили тела. – Rene

+0

. Я бы сказал, что-то вроде :-) На самом деле я сейчас работаю в проекте с открытым исходным кодом, где мы много делаем в оракуле. Проблема в том, что кто-то повредил свою базу данных и хочет начать с новой установки. Затем вам нужно загрузить из svn связку (> 100) из sql-источников. Невозможно установить их вручную, поэтому я хочу написать некоторую install.sql, где людям не нужно заботиться о последовательности сбрасывания. Может быть, есть лучший способ, о котором я думал -> получение зависимостей? – christian

0

Хорошо, у меня было время, чтобы снова посмотреть на работу, и я хочу поделиться результатами. Возможно, кто-то сталкивается с этой нитью, ища решение. Прежде всего, я использовал SQL как SYS, но я думаю, что вы можете сделать это в каждой схеме, используя общедоступные синонимы.

Процедура "exec obj_install_seq.make_install ('SCOTT');" делает clob, содержащий sql + совместимый sql-файл, предполагая, что ваши источники называются «object_name.object_type.sql». Просто размотайте его.

Приветствия Chris

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; 
/
Смежные вопросы