web-dev-qa-db-fra.com

Comment trouver des dépendances de clé étrangère pointant vers un enregistrement dans Oracle?

J'ai une très grande base de données Oracle, avec beaucoup de tables et des millions de lignes. Je dois supprimer l'une d'entre elles, mais je veux m'assurer que sa suppression ne cassera pas les autres lignes dépendantes qui le désignent comme un enregistrement de clé étrangère. Existe-t-il un moyen d’obtenir une liste de tous les autres enregistrements, ou au moins des schémas de table, qui pointent vers cette ligne? Je sais que je pourrais simplement essayer de le supprimer moi-même et attraper l'exception, mais je ne vais pas exécuter le script moi-même et je dois en avoir besoin pour la première fois. 

J'ai les outils SQL Developer d'Oracle et PL/SQL Developer de AllRoundAutomations à ma disposition.

Merci d'avance!

19
daveslab

Je regarde toujours les clés étrangères pour la table de départ et je reviens en arrière. Les outils de base de données ont généralement un nœud de dépendances ou de contraintes. Je sais que PL/SQL Developer a une façon de voir les FK, mais cela fait longtemps que je ne les utilise pas, je ne peux donc pas l'expliquer.

il suffit de remplacer XXXXXXXXXXXX par un nom de table ...

/* The following query lists all relationships */ 

select
 a.owner||'.'||a.table_name "Referenced Table"
,b.owner||'.'||b.table_name "Referenced by"
,b.constraint_name "Foreign Key"
from all_constraints a, all_constraints b 
where 
b.constraint_type = 'R'
and a.constraint_name = b.r_constraint_name 
and b.table_name='XXXXXXXXXXXX' -- Table name 
order by a.owner||'.'||a.table_name
26
Eric Schneider

Voici ma solution pour lister toutes les références à une table:

select
  src_cc.owner as src_owner,
  src_cc.table_name as src_table,
  src_cc.column_name as src_column,
  dest_cc.owner as dest_owner,
  dest_cc.table_name as dest_table,
  dest_cc.column_name as dest_column,
  c.constraint_name
from
  all_constraints c
inner join all_cons_columns dest_cc on
  c.r_constraint_name = dest_cc.constraint_name
  and c.r_owner = dest_cc.owner
inner join all_cons_columns src_cc on
  c.constraint_name = src_cc.constraint_name
  and c.owner = src_cc.owner
where
  c.constraint_type = 'R'
  and dest_cc.owner = 'MY_TARGET_SCHEMA'
  and dest_cc.table_name = 'MY_TARGET_TABLE'
  --and dest_cc.column_name = 'MY_OPTIONNAL_TARGET_COLUMN'
;

Avec cette solution, vous avez également les informations de quelle colonne de quelle table fait référence à quelle colonne de votre table cible (et vous pouvez y filtrer).

30
zigarn

J'ai eu un problème similaire récemment, mais j'ai vite compris que trouver les dépendances directes ne suffisait pas. J'ai donc écrit une requête pour afficher un arbre de dépendances de clé étrangère à plusieurs niveaux:

SELECT LPAD(' ',4*(LEVEL-1)) || table1 || ' <-- ' || table2 tables, table2_fkey
FROM
  (SELECT a.table_name table1, b.table_name table2, b.constraint_name table2_fkey
  FROM user_constraints a, user_constraints b 
  WHERE a.constraint_type IN('P', 'U') 
  AND b.constraint_type = 'R' 
  AND a.constraint_name = b.r_constraint_name 
  AND a.table_name != b.table_name
  AND b.table_name <> 'MYTABLE')
CONNECT BY PRIOR  table2 = table1 AND LEVEL <= 5
START WITH table1 = 'MYTABLE';

Cela donne un résultat comme celui-ci, lorsque SHIPMENT est utilisé comme MYTABLE dans ma base de données:

SHIPMENT <-- ADDRESS
SHIPMENT <-- PACKING_LIST
    PACKING_LIST <-- PACKING_LIST_DETAILS
    PACKING_LIST <-- PACKING_UNIT
        PACKING_UNIT <-- PACKING_LIST_ITEM
    PACKING_LIST <-- PO_PACKING_LIST
...
6
Donato Szilagyi

Nous pouvons utiliser le dictionnaire de données pour identifier les tables qui référencent la clé primaire de la table en question. À partir de là, nous pouvons générer du SQL dynamique pour interroger ces tables sur la valeur que nous voulons zapper:

SQL> declare
  2      n pls_integer;
  3      tot pls_integer := 0;
  4  begin
  5      for lrec in ( select table_name from user_constraints
  6                    where r_constraint_name = 'T23_PK' )
  7      loop
  8          execute immediate 'select count(*) from '||lrec.table_name
  9                              ||' where col2 = :1' into n using &&target_val;
 10          if n = 0 then
 11              dbms_output.put_line('No impact on '||lrec.table_name);
 12          else
 13              dbms_output.put_line('Uh oh! '||lrec.table_name||' has '||n||' hits!');
 14          end if;
 15          tot := tot + n;
 16      end loop;
 17      if tot = 0
 18      then
 19          delete from t23 where col2 = &&target_val;
 20          dbms_output.put_line('row deleted!');
 21      else
 22          dbms_output.put_line('delete aborted!');
 23      end if;
 24  end;
 25  /
Enter value for target_val: 6
No impact on T34
Uh oh! T42 has 2 hits!
No impact on T69
delete aborted!

PL/SQL procedure successfully completed.

SQL>

Cet exemple trompe un peu. Le nom de la clé primaire cible est codé en dur et la colonne de référence porte le même nom sur toutes les tables dépendantes. Résoudre ces problèmes est laissé comme un exercice pour le lecteur;)

3
APC

J'ai été surpris de constater à quel point il était difficile de trouver l'ordre de dépendance des tables en fonction des relations de clé étrangère. J'en avais besoin parce que je voulais supprimer les données de toutes les tables et les importer à nouveau. Voici la requête que j'ai écrite pour lister les tables par ordre de dépendance. J'ai été en mesure d'écrire les suppressions en utilisant la requête ci-dessous et de les importer à nouveau en utilisant les résultats de la requête dans l'ordre inverse. 

   SELECT referenced_table
         ,MAX(lvl) for_deleting
         ,MIN(lvl) for_inserting
   FROM
         ( -- Hierarchy of dependencies
         SELECT LEVEL lvl
               ,t.table_name referenced_table
               ,b.table_name referenced_by
         FROM user_constraints A
         JOIN user_constraints b 
               ON  A.constraint_name = b.r_constraint_name
               and b.constraint_type = 'R'
         RIGHT JOIN user_tables t
               ON  t.table_name = A.table_name
         START WITH b.table_name IS NULL
         CONNECT BY b.table_name = PRIOR t.table_name
         )
   GROUP BY referenced_table
   ORDER BY for_deleting, for_inserting;
0
daivrz

Les contraintes Oracle utilisent les index de table pour référencer les données.
Pour savoir quelles tables référencent une table, il suffit de rechercher l’index dans l’ordre inverse.

/* Toggle ENABLED and DISABLE status for any referencing constraint: */ 

select 'ALTER TABLE '||b.owner||'.'||b.table_name||' '||
        decode(b.status, 'ENABLED', 'DISABLE ', 'ENABLE ')||
       'CONSTRAINT '||b.constraint_name||';' 
  from all_indexes a,
       all_constraints b
 where a.table_name='XXXXXXXXXXXX' -- Table name 
   and a.index_name = b.r_constraint_name;

Obs .: La désactivation des références améliore considérablement le temps des commandes DML (mise à jour, suppression et insertion).

Cela peut aider beaucoup dans les opérations en bloc, où vous savez que toutes les données sont cohérentes.

/* List which columns are referenced in each constraint */

select ' TABLE "'||b.owner||'.'||b.table_name||'"'||
        '('||listagg (c.column_name, ',') within group (order by c.column_name)||')'|| 
        ' FK "'||b.constraint_name||'" -> '||a.table_name||
        ' INDEX "'||a.index_name||'"'
        "REFERENCES"
  from all_indexes a,
       all_constraints b,
       all_cons_columns c
 where rtrim(a.table_name) like 'XXXXXXXXXXXX' -- Table name 
   and a.index_name = b.r_constraint_name
   and c.constraint_name = b.constraint_name
 group by b.owner, b.table_name, b.constraint_name, a.table_name, a.index_name
 order by 1;
0
Luiz Vaz