Una herramienta de comparación de esquemas es una buena idea. El esquema de la base de datos es mucho más complicado de lo que la mayoría de la gente cree, y cada diferencia entre dos esquemas de base de datos tiene el potencial de causar errores.
Si todavía está interesado en hacerlo usted mismo, el mejor enfoque que he encontrado es extraer las definiciones del esquema al texto y luego ejecutar una comparación de texto. Siempre que todo esté ordenado alfabéticamente, puede usar la función Comparar documentos en Microsoft Word (o FC.EXE, DIFF o equivalente) para resaltar las diferencias.
El siguiente script SQLPlus genera la definición del esquema en orden alfabético, para permitir la comparación. Hay dos secciones. La primera sección enumera cada columna, en el formato:
table_name.column_name: data_type = data_default <nullable>
La segunda sección enumera índices y restricciones, de la siguiente manera:
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)
El script sirve como referencia útil para extraer algunos de los detalles del esquema de Oracle. Este puede ser un buen conocimiento cuando se encuentra en los sitios de los clientes y no tiene las herramientas habituales disponibles, o cuando las políticas de seguridad le impiden acceder a la base de datos del sitio del cliente directamente desde su propia PC.
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;
/
Lamentablemente, existen algunas limitaciones:
- Los retornos de carro y los espacios en blanco incorporados en data_defaults y las definiciones de restricciones de verificación pueden resaltarse como diferencias, aunque no tengan ningún efecto en el esquema.
- No incluye claves alternativas, índices únicos o índices de rendimiento. Esto requeriría una tercera instrucción SELECT en el script, haciendo referencia a las vistas de catálogo all_ind_columns y all_indexes.
- No incluye detalles de seguridad, sinónimos, paquetes, disparadores, etc. Los paquetes y disparadores se compararían mejor con un enfoque similar al que usted propuso originalmente. Se podrían agregar otros aspectos de la definición del esquema al script anterior.
- Las definiciones de FK anteriores identifican las columnas de clave externa de referencia, pero no la PK o la tabla a la que se hace referencia. Solo un detalle más que nunca llegué a hacer.
Incluso si no usa el script. Hay un cierto placer tecnológico en jugar con estas cosas.;-)
Mateo