Ett verktyg för schemajämförelse är en bra idé. Databasschemat är mycket mer komplicerat än vad de flesta ger kredit, och varje skillnad mellan två databasscheman har potential att orsaka buggar.
Om du fortfarande är sugen på att göra det själv är det bästa sättet jag har hittat att extrahera schemadefinitionerna till text och sedan köra en textjämförelse. Så länge allt är sorterat i alfabetisk ordning kan du sedan använda funktionen Jämför dokument i Microsoft Word (eller FC.EXE, DIFF eller motsvarande) för att markera skillnaderna.
Följande SQLPlus-skript matar ut schemadefinitionen alfabetiskt för att möjliggöra jämförelse. Det finns två sektioner. Det första avsnittet listar varje kolumn, i formatet:
table_name.column_name: data_type = data_default <nullable>
Det andra avsnittet listar index och begränsningar enligt följande:
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)
Skriptet fungerar som en användbar referens för att extrahera några av Oracle-schemadetaljerna. Detta kan vara bra att ha när du är ute på klientwebbplatser och du inte har dina vanliga verktyg tillgängliga, eller när säkerhetspolicyer hindrar dig från att komma åt en klientwebbplatsdatabas direkt från din egen dator.
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;
/
Tyvärr finns det några begränsningar:
- Inbäddade vagnreturer och blanksteg i data_defaults, och kontrollbegränsningsdefinitioner, kan markeras som skillnader, även om de inte har någon effekt på schemat.
- Innehåller inte alternativa nycklar, unika index eller prestandaindex. Detta skulle kräva en tredje SELECT-sats i skriptet, som hänvisar till all_ind_columns och all_indexes katalogvyer.
- Innehåller inte säkerhetsdetaljer, synonymer, paket, utlösare etc. Paket och utlösare skulle bäst jämföras med ett tillvägagångssätt som liknar det du ursprungligen föreslog. Andra aspekter av schemadefinitionen kan läggas till ovanstående skript.
- FK-definitionerna ovan identifierar referenskolumnerna för främmande nyckel, men inte PK eller tabellen som refereras till. Bara en detalj till som jag aldrig hann göra.
Även om du inte använder skriptet. Det finns ett visst tekniskt nöje att leka med det här.;-)
Matteus