Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p>A schema comparison tool is a good idea. The database schema is far more complicated than most people give credit, and every difference between two database schemas has the potential to cause bugs.</p> <p>If you're still keen to do it yourself, the best approach I've found is to extract the schema definitions to text, then run a text compare. As long as everything is sorted alphabetically, you can then use Compare Documents feature in Microsoft Word (or FC.EXE, DIFF or equivalent), to highlight the differences.</p> <p>The following SQLPlus script outputs the schema definition alphabetically, to allow comparison. There are two sections. The first section lists each column, in the format:</p> <pre><code>table_name.column_name: data_type = data_default &lt;nullable&gt; </code></pre> <p>The second section lists indexes and constraints, as follows:</p> <pre><code>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) </code></pre> <p>The script serves as a useful references for extracting some of the Oracle schema details. This can be good knowledge to have when you're out at client sites and you don't have your usual tools available, or when security policies prevent you from accessing a client site database directly from your own PC.</p> <pre><code>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|| ' &lt;'||c_columnRow.nullable||'&gt;',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||') ') &gt; 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; / </code></pre> <p>Unfortunately, there are a few limitations:</p> <ol> <li>Embedded carriage returns and whitespace in data_defaults, and check constraint definitions, may be highlighted as differences, even though they have zero effect on the schema.</li> <li>Does not include alternate keys, unique indexes or performance indexes. This would require a third SELECT statement in the script, referencing all_ind_columns and all_indexes catalog views.</li> <li>Does not include security details, synonyms, packages, triggers, etc. Packages and triggers would be best compared using an approach similar to the one you originally proposed. Other aspects of the schema definition could be added to the above script.</li> <li>The FK definitions above identify the referencing foreign key columns, but not the PK or the table being referenced. Just one more detail I never got around to doing.</li> </ol> <p>Even if you don't use the script. There's a certain techie pleasure in playing with this stuff. ;-)</p> <p>Matthew</p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload