Oracle
 sql >> Base de données >  >> RDS >> Oracle

Comparez deux schémas et mettez à jour l'ancien schéma avec les nouvelles colonnes du nouveau schéma

Un outil de comparaison de schéma est une bonne idée. Le schéma de base de données est bien plus compliqué que ce que la plupart des gens pensent, et chaque différence entre deux schémas de base de données peut potentiellement provoquer des bogues.

Si vous souhaitez toujours le faire vous-même, la meilleure approche que j'ai trouvée consiste à extraire les définitions de schéma en texte, puis à exécuter une comparaison de texte. Tant que tout est trié par ordre alphabétique, vous pouvez ensuite utiliser la fonction de comparaison de documents dans Microsoft Word (ou FC.EXE, DIFF ou équivalent) pour mettre en évidence les différences.

Le script SQLPlus suivant génère la définition de schéma par ordre alphabétique, pour permettre la comparaison. Il y a deux sections. La première section répertorie chaque colonne, au format :

table_name.column_name: data_type = data_default <nullable>

La deuxième section répertorie les index et les contraintes, comme suit :

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)

Le script sert de référence utile pour extraire certains des détails du schéma Oracle. Cela peut être une bonne connaissance à avoir lorsque vous êtes sur des sites clients et que vous ne disposez pas de vos outils habituels, ou lorsque les politiques de sécurité vous empêchent d'accéder à une base de données de site client directement depuis votre propre 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;
/

Malheureusement, il y a quelques limitations :

  1. Les retours chariot et les espaces blancs intégrés dans data_defaults, ainsi que les définitions de contraintes de vérification, peuvent être mis en évidence comme des différences, même s'ils n'ont aucun effet sur le schéma.
  2. N'inclut pas les clés alternatives, les index uniques ou les index de performance. Cela nécessiterait une troisième instruction SELECT dans le script, faisant référence aux vues de catalogue all_ind_columns et all_indexes.
  3. N'inclut pas les détails de sécurité, les synonymes, les packages, les déclencheurs, etc. Les packages et les déclencheurs seraient mieux comparés en utilisant une approche similaire à celle que vous avez initialement proposée. D'autres aspects de la définition du schéma pourraient être ajoutés au script ci-dessus.
  4. Les définitions FK ci-dessus identifient les colonnes de clé étrangère de référence, mais pas la PK ou la table référencée. Juste un détail de plus que je n'ai jamais eu le temps de faire.

Même si vous n'utilisez pas le script. Il y a un certain plaisir technique à jouer avec ce truc.;-)

Matthieu