Dba compare schemas

De wiki.nexiat.fr
Aller à la navigation Aller à la recherche
Fiche express
Type Script SQL*Plus de diagnostic
Domaine Administration (comparaison de schémas)
Voir aussi Dba column constraints · Dba errors

Dba_compare_schemas compare intégralement deux schémas Oracle — le schéma local (celui utilisé pour se connecter) et un schéma distant accessible via un database link temporaire — et produit un rapport détaillé de tous les écarts constatés : objets présents d'un seul côté, objets invalides, privilèges (rôles, système, objets), colonnes de table (présence, type, nullabilité...), index, contraintes, séquences, synonymes privés, code source PL/SQL (packages, procédures, fonctions), triggers, vues, jobs planifiés et database links.

Le script a été conçu et testé sur des versions Oracle allant de 7.3 à 11g. Certains éléments ne sont volontairement pas comparés en détail (commentaires, partitions, types objets, tables imbriquées, dimensions, clusters, métadonnées d'audit, IOT, tables temporaires, snapshots/vues matérialisées) : ils apparaissent toutefois dans la section « Résumé des objets » en début de rapport.

Fonctionnement

  1. Se connecter à la base en tant que l'un des deux schémas à comparer (le schéma « local »).
  2. Exécuter le script : il demande confirmation, puis les informations de connexion du schéma « distant » (utilisateur, mot de passe, nom de service Oracle Net) et enfin le nom du fichier de rapport (un nom par défaut est proposé).
  3. Le script crée temporairement un database link (remote_schema_link), une table de travail (schema_compare_temp) et deux fonctions PL/SQL utilitaires (getLongText/getLongText2, utilisées pour comparer le texte des vues au-delà de la limite de 4000 caractères d'un LONG classique).
  4. Il génère le rapport section par section (spoolé dans le fichier choisi), puis supprime tous les objets temporaires créés en début d'exécution.

Prérequis : le compte utilisé doit disposer des privilèges nécessaires pour créer un database link, une table et des fonctions dans son propre schéma, ainsi que d'un accès en lecture aux vues USER_*/ALL_OBJECTS des deux côtés de la liaison.

Script

SET PAGESIZE  50000
SET LINESIZE  256

PROMPT
PROMPT +------------------------------------------------------------------------+
PROMPT | SCRIPT DE COMPARAISON DE SCHEMAS                                       |
PROMPT |------------------------------------------------------------------------|
PROMPT |                                                                        |
PROMPT | UTILISATION                                                           |
PROMPT | -----------------------------------------------------------------------|
PROMPT | Ce script doit être exécuté en étant connecté à la base Oracle en tant |
PROMPT | que l'un des deux schémas à comparer. Il demandera ensuite le nom      |
PROMPT | utilisateur, le mot de passe et le nom du service Oracle Net (TNS) du  |
PROMPT | second schéma (distant) à comparer. Enfin, il demandera le nom du      |
PROMPT | fichier de rapport à générer pour toutes les différences détectées.    |
PROMPT | (Appuyez sur [ENTREE] pour accepter le nom de fichier par défaut.)     |
PROMPT |                                                                        |
PROMPT | REMARQUE                                                              |
PROMPT | -----------------------------------------------------------------------|
PROMPT | Les objets suivants seront créés temporairement pour les besoins du    |
PROMPT | script.                                                                |
PROMPT |                                                                        |
PROMPT |     [*] Database Link      (remote_schema_link)                       |
PROMPT |     [*] Table              (schema_compare_temp)                      |
PROMPT |     [*] Procédure PL/SQL   (getLongText)                              |
PROMPT |     [*] Procédure PL/SQL   (getLongText2)                             |
PROMPT |                                                                        |
PROMPT | Ces objets sont supprimés à la fin du script.                          |
PROMPT +------------------------------------------------------------------------+
PROMPT

SET TERMOUT OFF
COLUMN local_conn_info NEW_VALUE local_conn_info NOPRINT
SELECT  'Vous êtes actuellement connecté à l''instance [' ||
        SYS_CONTEXT('USERENV', 'INSTANCE_NAME')  || '] en tant qu''utilisateur [' ||
        SYS_CONTEXT('USERENV', 'SESSION_USER') || '].' local_conn_info
FROM   dual;
SET TERMOUT ON

PROMPT +------------------------------------------------------------------------+
PROMPT | CONNEXION LOCALE                                                       |
PROMPT |------------------------------------------------------------------------|
PROMPT | &local_conn_info
PROMPT +------------------------------------------------------------------------+
PROMPT

ACCEPT a1 CHAR PROMPT "Appuyez sur <ENTREE> pour continuer ou CTRL-C pour quitter... "
PROMPT

REM +---------------------------------------------------------------------------+
REM | DEMANDE DU NOM UTILISATEUR, MOT DE PASSE ET NOM DE SERVICE ORACLE NET.    |
REM +---------------------------------------------------------------------------+

ACCEPT schema   CHAR  PROMPT "Nom utilisateur du schéma distant : "
ACCEPT password CHAR  PROMPT "Mot de passe du schéma distant    : " HIDE
ACCEPT tns_name CHAR  PROMPT "Service Oracle Net du schéma distant : "

REM +---------------------------------------------------------------------------+
REM | CREATION D'UN DATABASE LINK TEMPORAIRE.                                   |
REM +---------------------------------------------------------------------------+

SET FEEDBACK OFF
SET VERIFY OFF
SET TRIMSPOOL ON

CREATE DATABASE LINK remote_schema_link
    CONNECT TO &schema IDENTIFIED BY &password
    USING '&tns_name'
/

REM +---------------------------------------------------------------------------+
REM | NOM DE FICHIER DE RAPPORT PAR DEFAUT (MODIFIABLE PAR L'UTILISATEUR).      |
REM +---------------------------------------------------------------------------+

SET TERMOUT OFF
COLUMN dflt_name NEW_VALUE dflt_name NOPRINT
SELECT 'compare_'       ||
       LOWER(user)      || '_' ||
       LOWER('&schema') || '_' ||
       LOWER('&tns_name') dflt_name
FROM   dual;
SET TERMOUT ON

PROMPT +------------------------------------------------------------------------+
PROMPT | NOM DU FICHIER DE RAPPORT                                              |
PROMPT |------------------------------------------------------------------------|
PROMPT | Le nom de fichier par défaut est &dflt_name..lst
PROMPT |                                                                        |
PROMPT | Pour l'utiliser, appuyez sur [ENTREE], sinon saisissez un autre nom.    |
PROMPT +------------------------------------------------------------------------+
PROMPT

SET HEADING OFF
COLUMN report_name NEW_VALUE report_name NOPRINT
SELECT
    'Nom de rapport utilisé : ' || NVL('&&report_name', '&dflt_name')
  , NVL('&&report_name', '&dflt_name') || '.lst' report_name
FROM sys.dual;
SPOOL &report_name
SET HEADING ON

REM +---------------------------------------------------------------------------+
REM | EN-TETE DU RAPPORT : DATE, HEURE, INFOS DE CONNEXION.                     |
REM +---------------------------------------------------------------------------+

SELECT SUBSTR(RPAD(TO_CHAR(sysdate, 'DD-MON-YYYY HH24:MI:SS'), 25), 1, 25) "Date et heure du rapport"
FROM   dual;

COLUMN local_schema  FORMAT a45 HEADING "Schéma local"  TRUNC
COLUMN remote_schema FORMAT a45 HEADING "Schéma distant" TRUNC

SELECT
    user       || '@' || c.global_name  local_schema
  , a.username || '@' || b.global_name  remote_schema
FROM
    user_users@remote_schema_link   a
  , global_name@remote_schema_link  b
  , global_name                     c
WHERE
    rownum = 1;

SET FEEDBACK OFF
SET TERMOUT OFF

COLUMN object_name FORMAT a40         HEADING 'Nom objet'
COLUMN object_type FORMAT a40         HEADING 'Type objet'
COLUMN obj_count    FORMAT 999,999,999 HEADING 'Nombre objets'

PROMPT
SET HEADING OFF
SET FEEDBACK OFF
SELECT '+----------------------------------------------------------------------+' || chr(10) ||
       '|                       RESUME DES OBJETS                              |' || chr(10) ||
       '+----------------------------------------------------------------------+'
FROM    dual;
SET HEADING ON
SET FEEDBACK ON

PROMPT
PROMPT ========================================================
PROMPT Objets absents du schéma local - (Résumé)
PROMPT ========================================================

SELECT
    object_type
  , count(*)    obj_count
FROM
    (SELECT
         object_type
       , DECODE(  object_type
                , 'INDEX', DECODE(SUBSTR(object_name, 1, 5), 'SYS_C', 'SYS_C', object_name)
                , 'LOB'  , DECODE(SUBSTR(object_name, 1, 7), 'SYS_LOB', 'SYS_LOB', object_name)
                , object_name)
     FROM  user_objects@remote_schema_link
     MINUS
     SELECT
         object_type
       , DECODE(  object_type
                , 'INDEX', DECODE(SUBSTR(object_name, 1, 5), 'SYS_C', 'SYS_C', object_name)
                , 'LOB',   DECODE(SUBSTR(object_name, 1, 7), 'SYS_LOB', 'SYS_LOB', object_name),
                 object_name)
     FROM user_objects
    )
GROUP BY object_type
ORDER BY object_type;

PROMPT
PROMPT
PROMPT ========================================================
PROMPT Objets en trop dans le schéma local - (Résumé)
PROMPT ========================================================

SELECT
    object_type
  , count(*)    obj_count
FROM
    (SELECT
         object_type
       , DECODE(   object_type
                 , 'INDEX', DECODE (SUBSTR (object_name, 1, 5), 'SYS_C',   'SYS_C',   object_name)
                 , 'LOB',   DECODE (SUBSTR (object_name, 1, 7), 'SYS_LOB', 'SYS_LOB', object_name)
                 , object_name)
     FROM   user_objects
     WHERE  object_type != 'DATABASE LINK'
        OR  object_name NOT LIKE 'REMOTE_SCHEMA_LINK.%'
     MINUS
     SELECT
         object_type
       , DECODE(   object_type
                 , 'INDEX', DECODE (SUBSTR (object_name, 1, 5), 'SYS_C',   'SYS_C',   object_name)
                 , 'LOB',   DECODE (SUBSTR (object_name, 1, 7), 'SYS_LOB', 'SYS_LOB', object_name)
                 , object_name)
     FROM   user_objects@remote_schema_link
    )
GROUP BY object_type
ORDER BY object_type;

PROMPT
SET HEADING OFF
SET FEEDBACK OFF
SELECT '+----------------------------------------------------------------------+' || chr(10) ||
       '|                   DIFFERENCES DE PRIVILEGES                          |' || chr(10) ||
       '+----------------------------------------------------------------------+'
FROM    dual;
SET HEADING ON
SET FEEDBACK ON

COLUMN granted_role  FORMAT a30   HEADING 'Rôle accordé'
COLUMN default_role  FORMAT a22   HEADING 'Rôle par défaut'
COLUMN os_granted    FORMAT a11   HEADING 'Accordé OS'
COLUMN owner         FORMAT a30   HEADING 'Propriétaire'
COLUMN table_name    FORMAT a30   HEADING 'Table'
COLUMN schema        FORMAT a7    HEADING 'Schéma'
COLUMN grantee       FORMAT a30   HEADING 'Bénéficiaire'
COLUMN privilege     FORMAT a40   HEADING 'Privilège'
COLUMN grantable     FORMAT a10   HEADING 'Transmissible'
COLUMN admin_option  FORMAT a13   HEADING 'Option Admin'

PROMPT
PROMPT ========================================================
PROMPT Différences de privilèges sur les rôles
PROMPT ========================================================

(
  SELECT
      granted_role
    , 'Remote' schema
    , admin_option
    , default_role
    , os_granted
  FROM
      user_role_privs@remote_schema_link
  MINUS
  SELECT
      granted_role
    , 'Remote' schema
    , admin_option
    , default_role
    , os_granted
  FROM
      user_role_privs
)
UNION ALL
(
  SELECT
      granted_role
    , 'Local' schema
    , admin_option
    , default_role
    , os_granted
  FROM
      user_role_privs
  MINUS
  SELECT
      granted_role
    , 'Local' schema
    , admin_option
    , default_role
    , os_granted
  FROM
      user_role_privs@remote_schema_link
)
ORDER BY 1, 2;

PROMPT
PROMPT ========================================================
PROMPT Différences de privilèges système
PROMPT ========================================================

(
  SELECT
      privilege
    , 'Remote' schema
    , admin_option
  FROM
      user_sys_privs@remote_schema_link
  MINUS
  SELECT
      privilege
    , 'Remote' schema
    , admin_option
  FROM
      user_sys_privs
)
UNION ALL
(
  SELECT
      privilege
    , 'Local' schema
    , admin_option
  FROM
      user_sys_privs
  MINUS
  SELECT
      privilege
    , 'Local' schema
    , admin_option
  FROM
      user_sys_privs@remote_schema_link
)
ORDER BY 1, 2;

PROMPT
PROMPT ========================================================
PROMPT Différences de privilèges au niveau objet
PROMPT ========================================================

(
  SELECT
      owner
    , table_name
    , 'Remote' schema
    , grantee
    , privilege
    , grantable
  FROM
      user_tab_privs@remote_schema_link
  WHERE
      (owner, table_name) IN (
          SELECT owner, object_name
          FROM   all_objects
      )
  MINUS
  SELECT
      owner
    , table_name
    , 'Remote' schema
    , grantee
    , privilege
    , grantable
  FROM     user_tab_privs
)
UNION ALL
(
  SELECT
      owner
    , table_name
    , 'Local' schema
    , grantee
    , privilege
    , grantable
  FROM
      user_tab_privs
  WHERE
      (owner, table_name) IN (
           SELECT owner, object_name
           FROM   all_objects@remote_schema_link
      )
  MINUS
  SELECT
      owner
    , table_name
    , 'Local' schema
    , grantee
    , privilege
    , grantable
  FROM
      user_tab_privs@remote_schema_link
)
ORDER BY 1, 2, 3;

PROMPT
SET HEADING OFF
SET FEEDBACK OFF
SELECT '+----------------------------------------------------------------------+' || chr(10) ||
       '|                     DIFFERENCES D''OBJETS                             |' || chr(10) ||
       '+----------------------------------------------------------------------+'
FROM    dual;
SET HEADING ON
SET FEEDBACK ON

PROMPT
PROMPT ========================================================
PROMPT Objets absents du schéma local
PROMPT ========================================================

SELECT
    DECODE(   object_type
            , 'INDEX', DECODE(SUBSTR(object_name, 1, 5), 'SYS_C', 'SYS_C', object_name)
            , 'LOB',   DECODE(SUBSTR(object_name, 1, 7), 'SYS_LOB', 'SYS_LOB', object_name)
            , object_name) object_name
  , object_type
FROM     user_objects@remote_schema_link
MINUS
SELECT
    DECODE(   object_type
            , 'INDEX', DECODE(SUBSTR(object_name, 1, 5), 'SYS_C', 'SYS_C', object_name)
            , 'LOB',   DECODE(SUBSTR(object_name, 1, 7), 'SYS_LOB', 'SYS_LOB', object_name)
            , object_name) object_name
  , object_type
FROM     user_objects
ORDER BY object_type, object_name;

PROMPT
PROMPT
PROMPT ========================================================
PROMPT Objets en trop dans le schéma local
PROMPT ========================================================

SELECT
    DECODE(   object_type
            , 'INDEX', DECODE(SUBSTR(object_name, 1, 5), 'SYS_C', 'SYS_C', object_name)
            , 'LOB',   DECODE(SUBSTR(object_name, 1, 7), 'SYS_LOB', 'SYS_LOB', object_name)
            , object_name) object_name
  , object_type
FROM
    user_objects
WHERE
     object_type != 'DATABASE LINK'
  OR object_name NOT LIKE 'REMOTE_SCHEMA_LINK.%'
MINUS
SELECT
    DECODE(   object_type
            , 'INDEX', DECODE(SUBSTR(object_name, 1, 5), 'SYS_C', 'SYS_C', object_name)
            , 'LOB',   DECODE(SUBSTR(object_name, 1, 7), 'SYS_LOB', 'SYS_LOB', object_name)
            , object_name) object_name
  , object_type
FROM
    user_objects@remote_schema_link
ORDER BY object_type, object_name;

PROMPT
PROMPT
PROMPT ========================================================
PROMPT Objets invalides dans le schéma local
PROMPT ========================================================

SELECT   object_name, object_type, status
FROM     user_objects
WHERE    status != 'VALID'
ORDER BY object_name, object_type;

PROMPT
PROMPT
PROMPT ========================================================
PROMPT Objets invalides dans le schéma distant
PROMPT ========================================================

SELECT   object_name, object_type, status
FROM     user_objects@remote_schema_link
WHERE    status != 'VALID'
ORDER BY object_name, object_type;

PROMPT
SET HEADING OFF
SET FEEDBACK OFF
SELECT '+----------------------------------------------------------------------+' || chr(10) ||
       '|                DIFFERENCES DE COLONNES DE TABLE                      |' || chr(10) ||
       '+----------------------------------------------------------------------+'
FROM    dual;
SET HEADING ON
SET FEEDBACK ON

PROMPT
PROMPT ========================================================
PROMPT Colonnes de table absentes d'un des deux schémas
PROMPT (les écarts ne sont pas listés dans l'ordre des colonnes)
PROMPT ========================================================

COLUMN table_name     FORMAT a30  HEADING 'Table'
COLUMN column_name    FORMAT a30  HEADING 'Colonne'
COLUMN mis             FORMAT a17  HEADING 'Absente du schéma'
COLUMN schema          FORMAT a7   HEADING 'Schéma'
COLUMN nullable        FORMAT a8   HEADING 'Nullable ?'
COLUMN data_type       FORMAT a9   HEADING 'Type'
COLUMN data_length     FORMAT 9999 HEADING 'Longueur'
COLUMN data_precision  FORMAT 9999 HEADING 'Précision'
COLUMN data_scale      FORMAT 9999 HEADING 'Échelle'
COLUMN default_length  FORMAT 9999 HEADING 'Long. valeur défaut'

(
  SELECT
      table_name
    , column_name
    , 'Local'      mis
  FROM   user_tab_columns@remote_schema_link
  WHERE  table_name IN (
             SELECT table_name
             FROM   user_tables
         )
  MINUS
  SELECT
      table_name
    , column_name
    , 'Local'     mis
  FROM   user_tab_columns
)
UNION ALL
(
  SELECT
      table_name
    , column_name
    , 'Remote'  mis
  FROM   user_tab_columns
  WHERE  table_name IN (
             SELECT table_name
             FROM   user_tables@remote_schema_link
         )
  MINUS
  SELECT
      table_name
    , column_name
    , 'Remote'   mis
  FROM   user_tab_columns@remote_schema_link
)
ORDER BY 1, 2;

PROMPT
PROMPT ========================================================
PROMPT Différences de type pour les colonnes présentes dans les
PROMPT deux schémas
PROMPT ========================================================

(
  SELECT
      table_name
    , column_name
    , 'Remote' schema
    , nullable
    , data_type
    , data_length
    , data_precision
    , data_scale
    , default_length
  FROM  user_tab_columns@remote_schema_link
  WHERE (table_name, column_name) IN (
            SELECT table_name, column_name
            FROM   user_tab_columns
        )
  MINUS
  SELECT
      table_name
    , column_name
    , 'Remote' schema
    , nullable
    , data_type
    , data_length
    , data_precision
    , data_scale
    , default_length
  FROM  user_tab_columns
)
UNION ALL
(
  SELECT
      table_name
    , column_name
    , 'Local' schema
    , nullable
    , data_type
    , data_length
    , data_precision
    , data_scale
    , default_length
  FROM  user_tab_columns
  WHERE (table_name, column_name) IN (
             SELECT table_name, column_name
             FROM   user_tab_columns@remote_schema_link
         )
  MINUS
  SELECT
      table_name
    , column_name
    , 'Local' schema
    , nullable
    , data_type
    , data_length
    , data_precision
    , data_scale
    , default_length
  FROM  user_tab_columns@remote_schema_link
)
ORDER BY 1, 2, 3;

PROMPT
SET HEADING OFF
SET FEEDBACK OFF
SELECT '+----------------------------------------------------------------------+' || chr(10) ||
       '|                     DIFFERENCES D''INDEX                              |' || chr(10) ||
       '+----------------------------------------------------------------------+'
FROM    dual;
SET HEADING ON
SET FEEDBACK ON

COLUMN index_name       FORMAT a30   HEADING 'Index'
COLUMN schema           FORMAT a7    HEADING 'Schéma'
COLUMN uniquenes                     HEADING 'Unicité'
COLUMN table_name       FORMAT a30   HEADING 'Table'
COLUMN column_name      FORMAT a30   HEADING 'Colonne'
COLUMN column_position  FORMAT 999   HEADING 'Ordre'

PROMPT
PROMPT ========================================================
PROMPT Différences pour les index présents dans les deux schémas
PROMPT ========================================================

(
  SELECT
      a.index_name
    , 'Remote' schema
    , a.uniqueness
    , a.table_name
    , b.column_name
    , b.column_position
  FROM
      user_indexes@remote_schema_link      a
    , user_ind_columns@remote_schema_link  b
  WHERE
        a.index_name IN (
           SELECT index_name
           FROM   user_indexes
        )
    AND b.index_name = a.index_name
    AND b.table_name = a.table_name
  MINUS
  SELECT
      a.index_name
    , 'Remote' schema
    , a.uniqueness
    , a.table_name
    , b.column_name
    , b.column_position
  FROM
      user_indexes      a
    , user_ind_columns  b
  WHERE
        b.index_name = a.index_name
    AND b.table_name = a.table_name
)
UNION ALL
(
  SELECT
      a.index_name
    , 'Local' schema
    , a.uniqueness
    , a.table_name
    , b.column_name
    , b.column_position
  FROM
      user_indexes      a
    , user_ind_columns  b
  WHERE
        a.index_name IN (
            SELECT index_name
            FROM   user_indexes@remote_schema_link
        )
    AND b.index_name = a.index_name
    AND b.table_name = a.table_name
  MINUS
  SELECT
      a.index_name
    , 'Local' schema
    , a.uniqueness
    , a.table_name
    , b.column_name
    , b.column_position
  FROM
      user_indexes@remote_schema_link      a
    , user_ind_columns@remote_schema_link  b
  WHERE
        b.index_name = a.index_name
    AND b.table_name = a.table_name
)
ORDER BY 1, 2, 6;

PROMPT
SET HEADING OFF
SET FEEDBACK OFF
SELECT '+----------------------------------------------------------------------+' || chr(10) ||
       '|                  DIFFERENCES DE CONTRAINTES                          |' || chr(10) ||
       '+----------------------------------------------------------------------+'
FROM    dual;
SET HEADING ON
SET FEEDBACK ON

PROMPT
PROMPT ========================================================
PROMPT Différences de contraintes pour les tables présentes dans
PROMPT les deux schémas
PROMPT ========================================================

SET FEEDBACK OFF

CREATE TABLE schema_compare_temp (
    database     NUMBER(1)
  , object_name  VARCHAR2(30)
  , object_text  VARCHAR2(2000)
  , hash_value   NUMBER
)
/

DECLARE

    CURSOR c1 IS
        SELECT constraint_name, search_condition
        FROM   user_constraints
        WHERE  search_condition IS NOT NULL;

    CURSOR c2 IS
        SELECT constraint_name, search_condition
        FROM   user_constraints@remote_schema_link
        WHERE  search_condition IS NOT NULL;

    v_constraint_name  VARCHAR2(30);
    v_search_condition VARCHAR2(32767);

BEGIN

    OPEN c1;
    LOOP
        FETCH c1 INTO v_constraint_name, v_search_condition;
        EXIT WHEN c1%NOTFOUND;

        v_search_condition := SUBSTR (v_search_condition, 1, 2000);
        INSERT INTO schema_compare_temp (
            database, object_name, object_text
        ) VALUES (
            1, v_constraint_name, v_search_condition
        );
    END LOOP;
    CLOSE c1;

    OPEN c2;
    LOOP
        FETCH c2 INTO v_constraint_name, v_search_condition;
        EXIT WHEN c2%NOTFOUND;
        v_search_condition := SUBSTR (v_search_condition, 1, 2000);
        INSERT INTO schema_compare_temp (
            database, object_name, object_text
        ) VALUES (
            2, v_constraint_name, v_search_condition
        );
    END LOOP;
    CLOSE c2;

  COMMIT;
END;
/

SET FEEDBACK ON

COLUMN constraint_name   FORMAT a30   HEADING 'Contrainte|Nom'
COLUMN schema            FORMAT a7    HEADING 'Schéma'
COLUMN constraint_type   FORMAT a10   HEADING 'Contrainte|Type'
COLUMN table_name        FORMAT a30   HEADING 'Table|Nom'
COLUMN r_constraint_name FORMAT a30   HEADING 'Contrainte réf.|Nom'
COLUMN delete_rule       FORMAT a10   HEADING 'Règle|Suppr.'
COLUMN status            FORMAT a9    HEADING 'Statut'
COLUMN object_text       FORMAT a20   HEADING 'Texte|Objet'

(
  SELECT
      REPLACE(TRANSLATE(a.constraint_name,'012345678','999999999'), '9', NULL) constraint_name
    , 'Remote' schema
    , a.constraint_type
    , a.table_name
    , a.r_constraint_name
    , a.delete_rule
    , a.status
    , b.object_text
  FROM
      user_constraints@remote_schema_link  a
    , schema_compare_temp                  b
  WHERE
        a.table_name IN (
            SELECT table_name
            FROM   user_tables
        )
    AND b.database(+)    = 2
    AND b.object_name(+) = a.constraint_name
  MINUS
  SELECT
      REPLACE(TRANSLATE(a.constraint_name,'012345678','999999999'), '9', NULL) constraint_name
    , 'Remote' schema
    , a.constraint_type
    , a.table_name
    , a.r_constraint_name
    , a.delete_rule
    , a.status
    , b.object_text
  FROM
      user_constraints     a
    , schema_compare_temp  b
  WHERE
        b.database(+)    = 1
    AND b.object_name(+) = a.constraint_name
)
UNION ALL
(
  SELECT
      REPLACE(TRANSLATE(a.constraint_name,'012345678','999999999'), '9', NULL) constraint_name
    , 'Local' schema
    , a.constraint_type
    , a.table_name
    , a.r_constraint_name
    , a.delete_rule
    , a.status
    , b.object_text
  FROM
      user_constraints     a
    , schema_compare_temp  b
  WHERE
        a.table_name IN (
            SELECT table_name
            FROM   user_tables@remote_schema_link
        )
    AND b.database(+)    = 1
    AND b.object_name(+) = a.constraint_name
  MINUS
  SELECT
      REPLACE(TRANSLATE(a.constraint_name,'012345678','999999999'), '9', NULL) constraint_name
    , 'Local' schema
    , a.constraint_type
    , a.table_name
    , a.r_constraint_name
    , a.delete_rule
    , a.status
    , b.object_text
  FROM
      user_constraints@remote_schema_link  a
    , schema_compare_temp                  b
  WHERE
        b.database(+)    = 2
    AND b.object_name(+) = a.constraint_name
)
ORDER BY 1, 4, 2;

PROMPT
SET HEADING OFF
SET FEEDBACK OFF
SELECT '+----------------------------------------------------------------------+' || chr(10) ||
       '|                   DIFFERENCES DE SEQUENCES                           |' || chr(10) ||
       '+----------------------------------------------------------------------+'
FROM    dual;
SET HEADING ON
SET FEEDBACK ON

PROMPT
PROMPT ========================================================
PROMPT Différences de séquences
PROMPT ========================================================

COLUMN sequence_name  FORMAT a30  HEADING 'Séquence|Nom'
COLUMN schema          FORMAT a7   HEADING 'Schéma'
COLUMN min_value                   HEADING 'Val.|Min'
COLUMN max_value                   HEADING 'Val.|Max'
COLUMN increment_by                HEADING 'Pas'
COLUMN cycle_flag     FORMAT a5    HEADING 'Cycle'
COLUMN order_flag     FORMAT a5    HEADING 'Ordre'
COLUMN cache_size                  HEADING 'Cache'

(
  SELECT
      sequence_name
    , 'Remote' schema
    , min_value
    , max_value
    , increment_by
    , cycle_flag
    , order_flag
    , cache_size
  FROM
      user_sequences@remote_schema_link
  MINUS
  SELECT
      sequence_name
    , 'Remote' schema
    , min_value
    , max_value
    , increment_by
    , cycle_flag
    , order_flag
    , cache_size
  FROM
      user_sequences
)
UNION ALL
(
  SELECT
      sequence_name
    , 'Local' schema
    , min_value
    , max_value
    , increment_by
    , cycle_flag
    , order_flag
    , cache_size
  FROM
      user_sequences
  MINUS
  SELECT
      sequence_name
    , 'Local' schema
    , min_value
    , max_value
    , increment_by
    , cycle_flag
    , order_flag
    , cache_size
  FROM
      user_sequences@remote_schema_link
)
ORDER BY 1, 2;

PROMPT
SET HEADING OFF
SET FEEDBACK OFF
SELECT '+----------------------------------------------------------------------+' || chr(10) ||
       '|               DIFFERENCES DE SYNONYMES PRIVES                        |' || chr(10) ||
       '+----------------------------------------------------------------------+'
FROM    dual;
SET HEADING ON
SET FEEDBACK ON

PROMPT
PROMPT ========================================================
PROMPT Différences de synonymes privés
PROMPT ========================================================

COLUMN synonym_name   FORMAT a30  HEADING 'Synonyme|Nom'
COLUMN schema          FORMAT a7   HEADING 'Schéma'
COLUMN table_owner     FORMAT a20  HEADING 'Table|Propriétaire'
COLUMN table_name      FORMAT a30  HEADING 'Table|Nom'
COLUMN db_link         FORMAT a25  HEADING 'DB|Link'

(
  SELECT
      synonym_name
    , 'Remote' schema
    , table_owner
    , table_name
    , db_link
  FROM
      user_synonyms@remote_schema_link
  MINUS
  SELECT
      synonym_name
    , 'Remote' schema
    , table_owner
    , table_name
    , db_link
  FROM     user_synonyms
)
UNION ALL
(
  SELECT
      synonym_name
    , 'Local' schema
    , table_owner
    , table_name
    , db_link
  FROM
      user_synonyms
  MINUS
  SELECT
      synonym_name
    , 'Local' schema
    , table_owner
    , table_name
    , db_link
  FROM
      user_synonyms@remote_schema_link
)
ORDER BY 1, 2;

PROMPT
SET HEADING OFF
SET FEEDBACK OFF
SELECT '+----------------------------------------------------------------------+' || chr(10) ||
       '|                    DIFFERENCES PL/SQL                                |' || chr(10) ||
       '+----------------------------------------------------------------------+'
FROM    dual;
SET HEADING ON
SET FEEDBACK ON

PROMPT
PROMPT ========================================================
PROMPT Différences de code source pour tous les packages,
PROMPT procédures et fonctions présents dans les deux schémas
PROMPT (COMPARAISON SENSIBLE A LA CASSE)
PROMPT ========================================================

COLUMN name           FORMAT a30          HEADING 'Source|Nom'
COLUMN type           FORMAT a20          HEADING 'Source|Type'
COLUMN discrepancies  FORMAT 999,999,999  HEADING 'Nombre|Écarts'

SELECT
    name
  , type
  , COUNT(*) discrepancies
FROM
    ( (  SELECT   name, type, line, text
         FROM     user_source@remote_schema_link
         WHERE    (name, type) IN (
             SELECT object_name, object_type
             FROM   user_objects
         )
         MINUS
         SELECT   name, type, line, text
         FROM     user_source
      )
      UNION ALL
      (  SELECT   name, type, line, text
         FROM     user_source
         WHERE    (name, type) IN (
             SELECT object_name, object_type
             FROM   user_objects@remote_schema_link
         )
         MINUS
         SELECT   name, type, line, text
         FROM     user_source@remote_schema_link
      )
    )
GROUP BY name, type
ORDER BY name, type;

PROMPT
PROMPT ========================================================
PROMPT Différences de code source pour tous les packages,
PROMPT procédures et fonctions présents dans les deux schémas
PROMPT (COMPARAISON INSENSIBLE A LA CASSE)
PROMPT ========================================================

COLUMN name           FORMAT a30          HEADING 'Source|Nom'
COLUMN type           FORMAT a20          HEADING 'Source|Type'
COLUMN discrepancies  FORMAT 999,999,999  HEADING 'Nombre|Écarts'

SELECT
    name
  , type
  , COUNT (*) discrepancies
FROM
    ( (  SELECT name, type, line, UPPER(text)
         FROM   user_source@remote_schema_link
         WHERE  (name, type) IN (
             SELECT object_name, object_type
             FROM   user_objects
         )
         MINUS
         SELECT name, type, line, UPPER(text)
         FROM   user_source
      )
      UNION ALL
      (  SELECT name, type, line, UPPER(text)
         FROM   user_source
         WHERE  (name, type) IN (
             SELECT object_name, object_type
             FROM   user_objects@remote_schema_link
         )
         MINUS
         SELECT name, type, line, UPPER(text)
         FROM   user_source@remote_schema_link
      )
    )
GROUP BY name, type
ORDER BY name, type;

PROMPT
SET HEADING OFF
SET FEEDBACK OFF
SELECT '+----------------------------------------------------------------------+' || chr(10) ||
       '|                   DIFFERENCES DE TRIGGERS                            |' || chr(10) ||
       '+----------------------------------------------------------------------+'
FROM    dual;
SET HEADING ON
SET FEEDBACK ON

PROMPT
PROMPT ========================================================
PROMPT Différences de triggers
PROMPT ========================================================

SET FEEDBACK OFF

TRUNCATE TABLE schema_compare_temp
/

DECLARE

    CURSOR c1 IS
        SELECT trigger_name, trigger_body
        FROM   user_triggers;

    CURSOR c2 IS
        SELECT trigger_name, trigger_body
        FROM   user_triggers@remote_schema_link;

    v_trigger_name VARCHAR2(30);
    v_trigger_body VARCHAR2(32767);
    v_hash_value   NUMBER;
BEGIN

    OPEN c1;
    LOOP
        FETCH c1 INTO v_trigger_name, v_trigger_body;
        EXIT WHEN c1%NOTFOUND;
        v_trigger_body := REPLACE(v_trigger_body, ' ', NULL);
        v_trigger_body := REPLACE(v_trigger_body, CHR(9), NULL);
        v_trigger_body := REPLACE(v_trigger_body, CHR(10), NULL);
        v_trigger_body := REPLACE(v_trigger_body, CHR(13), NULL);
        v_trigger_body := UPPER(v_trigger_body);
        v_hash_value := dbms_utility.get_hash_value(v_trigger_body, 1, 65536);
        INSERT INTO schema_compare_temp (
            database, object_name, hash_value
        ) VALUES (
            1, v_trigger_name, v_hash_value
        );
    END LOOP;
    CLOSE c1;

    OPEN c2;
    LOOP
        FETCH c2 INTO v_trigger_name, v_trigger_body;
        EXIT WHEN c2%NOTFOUND;
        v_trigger_body := REPLACE(v_trigger_body, ' ', NULL);
        v_trigger_body := REPLACE(v_trigger_body, CHR(9), NULL);
        v_trigger_body := REPLACE(v_trigger_body, CHR(10), NULL);
        v_trigger_body := REPLACE(v_trigger_body, CHR(13), NULL);
        v_trigger_body := UPPER(v_trigger_body);
        v_hash_value := dbms_utility.get_hash_value(v_trigger_body, 1, 65536);
        INSERT INTO schema_compare_temp (
            database, object_name, hash_value
        ) VALUES (
            2, v_trigger_name, v_hash_value
        );
    END LOOP;
    CLOSE c2;

END;
/

SET FEEDBACK ON

COLUMN trigger_name       FORMAT a20    HEADING 'Trigger|Nom'
COLUMN schema              FORMAT a7     HEADING 'Schéma'
COLUMN trigger_type        FORMAT a16    HEADING 'Trigger|Type'
COLUMN triggering_event    FORMAT a20    HEADING 'Événement|déclencheur'
COLUMN table_name          FORMAT a15    HEADING 'Table|Nom'
COLUMN referencing_names   FORMAT a20    HEADING 'Noms de|référence'
COLUMN when_clause         FORMAT a20    HEADING 'Clause|WHEN'
COLUMN status              FORMAT a9     HEADING 'Statut'
COLUMN hash_value                        HEADING 'Empreinte'

( SELECT
      a.trigger_name
    , 'Local' schema
    , a.trigger_type
    , SUBSTR(a.triggering_event, 1, 20)  triggering_event
    , a.table_name
    , SUBSTR(a.referencing_names, 1, 20) referencing_names
    , SUBSTR(a.when_clause, 1, 20)       when_clause
    , a.status
    , b.hash_value
  FROM
      user_triggers        a
    , schema_compare_temp  b
  WHERE
        b.object_name(+) = a.trigger_name
    AND b.database(+)    = 1
    AND a.table_name IN (
            SELECT table_name
            FROM   user_tables@remote_schema_link
        )
  MINUS
  SELECT
      a.trigger_name
    , 'Local' schema
    , a.trigger_type
    , SUBSTR(a.triggering_event, 1, 20)    triggering_event
    , a.table_name
    , SUBSTR(a.referencing_names, 1, 20)   referencing_names
    , SUBSTR(a.when_clause, 1, 20)         when_clause
    , a.status
    , b.hash_value
  FROM
      user_triggers@remote_schema_link  a
    , schema_compare_temp               b
  WHERE
        b.object_name(+) = a.trigger_name
    AND b.database(+)    = 2
)
UNION ALL
(
  SELECT
      a.trigger_name
    , 'Remote' schema
    , a.trigger_type
    , SUBSTR(a.triggering_event, 1, 20)    triggering_event
    , a.table_name
    , SUBSTR(a.referencing_names, 1, 20)   referencing_names
    , SUBSTR(a.when_clause, 1, 20)         when_clause
    , a.status
    , b.hash_value
  FROM
      user_triggers@remote_schema_link  a
    , schema_compare_temp               b
  WHERE
        b.object_name(+) = a.trigger_name
    AND b.database(+)    = 2
    AND a.table_name IN (
            SELECT table_name
            FROM   user_tables
        )
  MINUS
  SELECT
      a.trigger_name
    , 'Remote' schema
    , a.trigger_type
    , SUBSTR(a.triggering_event, 1, 20)    triggering_event
    , a.table_name
    , SUBSTR(a.referencing_names, 1, 20)   referencing_names
    , SUBSTR(a.when_clause, 1, 20)         when_clause
    , a.status
    , b.hash_value
  FROM
      user_triggers        a
    , schema_compare_temp  b
  WHERE
        b.object_name(+)  = a.trigger_name
    AND b.database(+)     = 1
)
ORDER BY 1, 2, 5, 3;

PROMPT
SET HEADING OFF
SET FEEDBACK OFF
SELECT '+----------------------------------------------------------------------+' || chr(10) ||
       '|                     DIFFERENCES DE VUES                              |' || chr(10) ||
       '+----------------------------------------------------------------------+'
FROM    dual;
SET HEADING ON
SET FEEDBACK ON

PROMPT
PROMPT ========================================================
PROMPT Différences pour les vues présentes dans les deux schémas
PROMPT ========================================================

SET FEEDBACK OFF
SET LONG 32767

TRUNCATE TABLE schema_compare_temp
/

CREATE OR REPLACE FUNCTION getLongText (    p_tname IN VARCHAR2
                                          , p_cname IN VARCHAR2
                                          , p_vname IN VARCHAR2) RETURN VARCHAR2
  AS
    l_sql       VARCHAR2(4000);
    l_cursor    INTEGER DEFAULT dbms_sql.open_cursor;
    l_n         NUMBER;
    l_long_val  VARCHAR2(4000);
    l_long_len  NUMBER;
    l_buflen    NUMBER := 4000;
    l_curpos    NUMBER := 0;
  BEGIN
    l_sql := 'select ' || p_cname || ' from ' || p_tname || ' where UPPER(view_name) = UPPER(:view_name)';
    DBMS_SQL.PARSE(   l_cursor
                    , l_sql
                    , DBMS_SQL.NATIVE);
    DBMS_SQL.BIND_VARIABLE(l_cursor, ':view_name', p_vname);
    DBMS_SQL.DEFINE_COLUMN_LONG(l_cursor, 1);
    l_n := DBMS_SQL.EXECUTE(l_cursor);

    IF (DBMS_SQL.FETCH_ROWS(l_cursor) > 0)
      THEN
        DBMS_SQL.COLUMN_VALUE_LONG(   l_cursor
                                    , 1
                                    , l_buflen
                                    , l_curpos
                                    , l_long_val
                                    , l_long_len);
    END IF;
    DBMS_SQL.CLOSE_CURSOR(l_cursor);
    RETURN l_long_val;
  END getLongText;
/

CREATE OR REPLACE FUNCTION getLongText2 (    p_tname IN VARCHAR2
                                           , p_cname IN VARCHAR2
                                           , p_vname IN VARCHAR2) RETURN VARCHAR2
  AS
    l_sql       VARCHAR2(4000);
    l_cursor    INTEGER DEFAULT dbms_sql.open_cursor;
    l_n         NUMBER;
    l_long_val  VARCHAR2(4000);
    l_long_len  NUMBER;
    l_buflen    NUMBER := 4000;
    l_curpos    NUMBER := 0;
  BEGIN
    l_sql := 'select ' || p_cname || ' from ' || p_tname || '@remote_schema_link where UPPER(view_name) = UPPER(:view_name)';
    DBMS_SQL.PARSE(   l_cursor
                    , l_sql
                    , DBMS_SQL.NATIVE);
    DBMS_SQL.BIND_VARIABLE(l_cursor, ':view_name', p_vname);
    DBMS_SQL.DEFINE_COLUMN_LONG(l_cursor, 1);
    l_n := DBMS_SQL.EXECUTE(l_cursor);

    IF (DBMS_SQL.FETCH_ROWS(l_cursor) > 0)
      THEN
        DBMS_SQL.COLUMN_VALUE_LONG(   l_cursor
                                    , 1
                                    , l_buflen
                                    , l_curpos
                                    , l_long_val
                                    , l_long_len);
    END IF;
    DBMS_SQL.CLOSE_CURSOR(l_cursor);
    RETURN l_long_val;
  END getLongText2;
/

DECLARE

    CURSOR c1 IS
        SELECT view_name, getLongText('USER_VIEWS', 'TEXT', view_name)
        FROM   user_views;

    CURSOR c2 IS
        SELECT view_name, getLongText2('USER_VIEWS', 'TEXT', view_name)
        FROM   user_views@remote_schema_link;

    v_view_name    VARCHAR2(30);
    v_text         VARCHAR2(32767);
    v_hash_value   NUMBER;

BEGIN

    OPEN c1;
    LOOP
        FETCH c1 INTO v_view_name, v_text;
        EXIT WHEN c1%NOTFOUND;
        v_hash_value := dbms_utility.get_hash_value(v_text, 1, 65536);
        INSERT INTO schema_compare_temp (
            database, object_name, object_text, hash_value
        ) VALUES (
            1, v_view_name, '[' || v_text || ']', v_hash_value
        );
    END LOOP;
    CLOSE c1;

    OPEN c2;
    LOOP
        FETCH c2 INTO v_view_name, v_text;
        EXIT WHEN c2%NOTFOUND;
        v_hash_value := dbms_utility.get_hash_value(v_text, 1, 65536);
        INSERT INTO schema_compare_temp (
            database, object_name, object_text, hash_value
        ) VALUES (
            2, v_view_name, '[' || v_text || ']', v_hash_value
        );
    END LOOP;
    CLOSE c2;

END;
/

SET FEEDBACK ON

COLUMN view_name  FORMAT a30    HEADING 'Vue|Nom'
COLUMN schema      FORMAT a7     HEADING 'Schéma'
COLUMN hash_value                HEADING 'Empreinte'

(
  SELECT
      a.view_name
    , 'Local' schema
    , b.hash_value
  FROM
      user_views           a
    , schema_compare_temp  b
  WHERE
        b.object_name(+) = a.view_name
    AND b.database(+)    = 1
    AND a.view_name IN (
            SELECT view_name
            FROM   user_views@remote_schema_link
        )
  MINUS
  SELECT
      a.view_name
    , 'Local' schema
    , b.hash_value
  FROM
      user_views@remote_schema_link  a
    , schema_compare_temp            b
  WHERE
        b.object_name(+)  = a.view_name
    AND b.database(+)     = 2
)
UNION ALL
(
  SELECT
      a.view_name
    , 'Remote' schema
    , b.hash_value
  FROM
      user_views@remote_schema_link  a
    , schema_compare_temp            b
  WHERE
        b.object_name(+) = a.view_name
    AND b.database(+)    = 2
    AND a.view_name IN (
         SELECT view_name
         FROM   user_views
     )
  MINUS
  SELECT
      a.view_name
    , 'Remote' schema
    , b.hash_value
  FROM
      user_views           a
    , schema_compare_temp  b
  WHERE
        b.object_name(+) = a.view_name
    AND b.database(+)    = 1
)
ORDER BY 1, 2;

PROMPT
SET HEADING OFF
SET FEEDBACK OFF
SELECT '+----------------------------------------------------------------------+' || chr(10) ||
       '|              DIFFERENCES DE FILES D''ATTENTE (JOBS)                   |' || chr(10) ||
       '+----------------------------------------------------------------------+'
FROM    dual;
SET HEADING ON
SET FEEDBACK ON

PROMPT
PROMPT ========================================================
PROMPT Différences sur les jobs planifiés
PROMPT ========================================================

COLUMN what     FORMAT a30   HEADING 'Action'
COLUMN interval FORMAT a30   HEADING 'Intervalle'
COLUMN broken   FORMAT a7    HEADING 'Cassé ?'

(
  SELECT
      what
    , interval
    , broken
    , 'Remote' schema
  FROM
      user_jobs@remote_schema_link
  MINUS
  SELECT
      what
    , interval
    , broken
    , 'Remote' schema
  FROM
      user_jobs
)
UNION ALL
(
  SELECT
      what
    , interval
    , broken
    , 'Local' schema
  FROM
      user_jobs
  MINUS
  SELECT
      what
    , interval
    , broken
    , 'Local' schema
  FROM
      user_jobs@remote_schema_link
)
ORDER BY 1, 2, 3;

PROMPT
SET HEADING OFF
SET FEEDBACK OFF
SELECT '+----------------------------------------------------------------------+' || chr(10) ||
       '|              DIFFERENCES DE DATABASE LINKS                           |' || chr(10) ||
       '+----------------------------------------------------------------------+'
FROM    dual;
SET HEADING ON
SET FEEDBACK ON

PROMPT
PROMPT ========================================================
PROMPT Différences de database links
PROMPT ========================================================

COLUMN db_link  FORMAT a30  HEADING 'DB Link'
COLUMN schema    FORMAT a7   HEADING 'Schéma'
COLUMN username  FORMAT a20  HEADING 'Utilisateur'
COLUMN host      FORMAT a20  HEADING 'Hôte'

(
  SELECT
      db_link
    , 'Remote' schema
    , username
    , host
  FROM
      user_db_links@remote_schema_link
  MINUS
  SELECT
      db_link
    , 'Remote' schema
    , username, host
  FROM
      user_db_links
)
UNION ALL
(
  SELECT
      db_link
    , 'Local' schema
    , username, host
  FROM
      user_db_links
  WHERE
      db_link NOT LIKE 'REMOTE_SCHEMA_LINK.%'
  MINUS
  SELECT
      db_link
    , 'Local' schema
    , username
    , host
  FROM
      user_db_links@remote_schema_link
)
ORDER BY 1, 2;

SPOOL OFF

SET TERMOUT ON

PROMPT
PROMPT =============
PROMPT FIN DU RAPPORT
PROMPT =============
PROMPT
PROMPT Rapport enregistré dans &report_name
PROMPT ==============================================================

SET FEEDBACK OFF

DROP TABLE schema_compare_temp;
DROP DATABASE LINK remote_schema_link;
DROP FUNCTION getLongText;
DROP FUNCTION getLongText2;

SET FEEDBACK 6

Colonnes / sortie

Le rapport est structuré en grandes sections, chacune annoncée par un bandeau +---+ :

  • Résumé des objets — comptage des objets manquants/en trop par type, des deux côtés.
  • Différences de privilèges — rôles, privilèges système, privilèges sur objets.
  • Différences d'objets — détail nommé des objets manquants, en trop, ou invalides.
  • Différences de colonnes de table — colonnes absentes ou de type différent entre les deux schémas.
  • Différences d'index et de contraintes — écarts de définition pour les objets existant des deux côtés.
  • Différences de séquences, de synonymes privés, de database links.
  • Différences PL/SQL — comparaison du code source (sensible et insensible à la casse) des packages/procédures/fonctions.
  • Différences de triggers et de vues — comparées via une empreinte (hash) du corps normalisé, pour ignorer les différences de mise en forme (espaces, tabulations, retours à la ligne).
  • Différences de jobs planifiés.

Points de vigilance

  • Le mot de passe du schéma distant est saisi en clair dans l'invite SQL*Plus (option HIDE, donc non affiché à l'écran) puis utilisé pour créer un database link — pensez à révoquer/recréer ce compte ou changer son mot de passe si le script est exécuté dans un contexte partagé.
  • Le script doit se terminer normalement pour que les objets temporaires (database link, table, fonctions) soient bien supprimés ; en cas d'interruption (CTRL-C, erreur), il faut les supprimer manuellement.
  • Sur un schéma volumineux, la comparaison du code source et des vues (hachage complet du texte) peut être longue.

Voir aussi

  • Dba column constraints — équivalent en plus léger, limité aux contraintes d'une seule table.
  • Dba errors — pour investiguer spécifiquement les objets invalides détectés par la section « Objets invalides ».