Инструмент за сравнение на схеми е добра идея. Схемата на базата данни е много по-сложна, отколкото повечето хора приписват, и всяка разлика между две схеми на база данни има потенциал да причини грешки.
Ако все още желаете да го направите сами, най-добрият подход, който открих, е да извлечете дефинициите на схемата в текст, след което да стартирате текстово сравнение. Докато всичко е подредено по азбучен ред, можете да използвате функцията за сравняване на документи в Microsoft Word (или FC.EXE, DIFF или еквивалент), за да подчертаете разликите.
Следният SQLPlus скрипт извежда дефиницията на схемата по азбучен ред, за да позволи сравнение. Има два раздела. Първият раздел изброява всяка колона във формат:
table_name.column_name: data_type = data_default <nullable>
Вторият раздел изброява индексите и ограниченията, както следва:
PK constraint_name on table_name (pk_column_list)
FK constraint_name on table_name (fk_column_list)
CHECK constraint_name on table_name (constraint_definition)
Скриптът служи като полезна справка за извличане на някои от подробностите на схемата на Oracle. Това може да е добра информация, която трябва да имате, когато сте навън в клиентски сайтове и не разполагате с обичайните си инструменти или когато политиките за сигурност ви пречат да получите достъп до база данни на клиентски сайт директно от собствения си компютър.
set serveroutput on;
set serveroutput on size 1000000;
declare
rowcnt pls_integer := 0;
cursor c_column is
select table_name, column_name, data_type,
data_precision, data_length, data_scale,
data_default, nullable,
decode(data_scale, null, null, ',') scale_comma,
decode(default_length, null, null, '= ') default_equals
from all_tab_columns where owner = 'BCC'
order by table_name, column_name;
cursor c_constraint is
select c.table_name, c.constraint_name,
decode(c.constraint_type,
'P','PK',
'R','FK',
'C','CHECK',
c.constraint_type) constraint_type,
c.search_condition,
cc.column_1||cc.comma_2||cc.column_2||cc.comma_3||cc.column_3||cc.comma_4||cc.column_4||
cc.comma_5||cc.column_5||cc.comma_6||cc.column_6||cc.comma_7||cc.column_7 r_columns
from all_constraints c,
( select owner, table_name, constraint_name, nvl(max(position),0) max_position,
max( decode( position, 1, column_name, null ) ) column_1,
max( decode( position, 2, decode(column_name, null, null, ',' ), null ) ) comma_2,
max( decode( position, 2, column_name, null ) ) column_2,
max( decode( position, 3, decode(column_name, null, null, ',' ), null ) ) comma_3,
max( decode( position, 3, column_name, null ) ) column_3,
max( decode( position, 4, decode(column_name, null, null, ',' ), null ) ) comma_4,
max( decode( position, 4, column_name, null ) ) column_4,
max( decode( position, 5, decode(column_name, null, null, ',' ), null ) ) comma_5,
max( decode( position, 5, column_name, null ) ) column_5,
max( decode( position, 6, decode(column_name, null, null, ',' ), null ) ) comma_6,
max( decode( position, 6, column_name, null ) ) column_6,
max( decode( position, 7, decode(column_name, null, null, ',' ), null ) ) comma_7,
max( decode( position, 7, column_name, null ) ) column_7
from all_cons_columns
group by owner, table_name, constraint_name ) cc
where c.owner = 'BCC'
and c.generated != 'GENERATED NAME'
and cc.owner = c.owner
and cc.table_name = c.table_name
and cc.constraint_name = c.constraint_name
order by c.table_name,
decode(c.constraint_type,
'P','PK',
'R','FK',
'C','CHECK',
c.constraint_type) desc,
c.constraint_name;
begin
for c_columnRow in c_column loop
dbms_output.put_line(substr(c_columnRow.table_name||'.'||c_columnRow.column_name||': '||
c_columnRow.data_type||'('||
nvl(c_columnRow.data_precision, c_columnRow.data_length)||
c_columnRow.scale_comma||c_columnRow.data_scale||') '||
c_columnRow.default_equals||c_columnRow.data_default||
' <'||c_columnRow.nullable||'>',1,255));
rowcnt := rowcnt + 1;
end loop;
for c_constraintRow in c_constraint loop
dbms_output.put_line(substr(c_constraintRow.constraint_type||' '||c_constraintRow.constraint_name||' on '||
c_constraintRow.table_name||' ('||
c_constraintRow.search_condition||
c_constraintRow.r_columns||') ',1,255));
if length(c_constraintRow.constraint_type||' '||c_constraintRow.constraint_name||' on '||
c_constraintRow.table_name||' ('||
c_constraintRow.search_condition||
c_constraintRow.r_columns||') ') > 255 then
dbms_output.put_line('... '||substr(c_constraintRow.constraint_type||' '||c_constraintRow.constraint_name||' on '||
c_constraintRow.table_name||' ('||
c_constraintRow.search_condition||
c_constraintRow.r_columns||') ',256,251));
end if;
rowcnt := rowcnt + 1;
end loop;
end;
/
За съжаление има няколко ограничения:
- Вградените връщания на каретка и интервали в data_defaults и дефиниции на ограничения за проверка могат да бъдат осветени като разлики, въпреки че нямат ефект върху схемата.
- Не включва алтернативни ключове, уникални индекси или индекси за ефективност. Това ще изисква трета команда SELECT в скрипта, препращаща към каталожни изгледи all_ind_columns и all_indexes.
- Не включва подробности за сигурността, синоними, пакети, тригери и т.н. Пакетите и тригерите биха били най-добре сравнени, като се използва подход, подобен на този, който първоначално сте предложили. Други аспекти на дефиницията на схемата могат да бъдат добавени към горния скрипт.
- Дефинициите на FK по-горе идентифицират препращащите колони с външен ключ, но не и PK или таблицата, към която се препраща. Само още една подробност, която така и не успях да направя.
Дори и да не използвате скрипта. Има известно техническо удоволствие да си играеш с тези неща.;-)
Матю