Comment trouver les dépendances clés étrangères pointant vers un enregistrement dans Oracle?

StackOverflow https://stackoverflow.com/questions/2509512

Question

J'ai une très grande base de données Oracle, avec beaucoup de tables et des millions de lignes. Je dois supprimer l'un d'eux, mais ils veulent faire en sorte que la laisser tomber ne se cassera pas d'autres lignes dépendantes qui pointent vers comme un enregistrement de clé étrangère. Y at-il un moyen d'obtenir une liste de tous les autres documents, ou au moins des schémas de table, ce point de cette ligne? Je sais que je pouvais essayer de le supprimer moi-même, et attraper l'exception, mais je ne vais pas courir le scénario et moi-même besoin pour exécuter propre la première fois.

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

Merci d'avance!

Était-ce utile?

La solution

Je regarde toujours les clés étrangères pour la table de départ et de travailler le chemin du retour. Les outils DB ont généralement un nœud de dépendances ou des contraintes. Je sais développeur PL / SQL est une façon de voir FK de, mais il a été un moment que je l'ai utilisé, donc je ne peux pas l'expliquer ...

il suffit de remplacer XXXXXXXXXXXX avec 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

Autres conseils

Voici ma solution à la liste 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 aussi les informations dont la colonne dont le tableau fait référence à quelle colonne de votre table cible (et vous pouvez filtrer sur elle).

J'ai eu un problème similaire récemment, mais connu bientôt, que trouver les dépendances directes ne suffit pas. J'ai donc écrit une requête pour afficher un arbre de dépendances de clés étrangères à 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';

Il donne un résultat comme celui-ci, lors de l'utilisation comme MYTABLE dans EXPEDITION 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
...

Nous pouvons utiliser le dictionnaire de données pour identifier les tableaux qui font référence à la clé primaire de la table en question. A partir de ce que nous pouvons générer une dynamique SQL pour interroger les tables pour 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 triche un peu. Le nom de la clé primaire cible est codé en dur, et la colonne de référencement a le même nom sur toutes les tables dépendantes. La résolution de ces problèmes est laissé comme un exercice pour le lecteur;)

avait une situation similaire. Dans mon cas, j'avais deux dossiers qui avaient fini avec le même ID différant seulement par cas. Je voulais vérifier ce que les enregistrements dépendants existe pour chacun de savoir qui était le plus facile à supprimer / mettre à jour

Les tirages suivants sur tous les enregistrements enfants pointant vers le dossier donné, par table enfant avec un compte pour chaque table / maître combinaison d'enregistrement

declare
  --
  -- Finds and prints out how many children there are per table and value for each value of a given field
  --

  -- Name of the table to base the query on  
  cTable      constant varchar2(20) := 'FOO';
  -- Name of the column to base the query on
  cCol        constant varchar2(10) := 'ID';
  -- Cursor to find interesting values (e.g. duplicates) in master table
  cursor cVals is
    select id 
    from foo f
    where exists (  select 1 from foo f2 
                    where upper(f.id) = upper(f2.id)
                    and f.rowid != f2.rowid );

  -- Everything below here should just work
  vNum        number(18,0);
  vSql        varchar2(4000);

  cOutColSize   number(2,0) := 30;

  cursor cReferencingTables is
    select
      consChild.table_name,
      consChild.constraint_name,
      colChild.column_name     
    from user_constraints consMast
    inner join user_constraints consChild on consMast.constraint_name = consChild.r_constraint_name  
    inner join USER_CONS_COLUMNS colChild on consChild.CONSTRAINT_NAME = colChild.CONSTRAINT_NAME
    inner join USER_CONS_COLUMNS colMast on colMast.CONSTRAINT_NAME = consMast.CONSTRAINT_NAME
    where consChild.constraint_type = 'R'      
      and consMast.table_name = cTable
      and colMast.column_name = cCol
    order by consMast.table_name, consChild.table_name;
begin

  dbms_output.put_line(
    rpad('Table', cOutColSize) || 
    rpad('Column', cOutColSize) || 
    rpad('Value', cOutColSize) || 
    rpad('Number', cOutColSize)
  );
  for rRef in cReferencingTables loop
    for rVals in cVals loop
      vSql := 'select count(1) from ' || rRef.table_name || ' where ' || rRef.column_name || ' = ''' || rVals.id || '''';
      execute immediate vSql into vNum;
      if vNum > 0 then
        dbms_output.put_line(
          rpad(rRef.table_name, cOutColSize) || 
          rpad(rRef.column_name, cOutColSize) || 
          rpad(rVals.id, cOutColSize) || 
          rpad(vNum, cOutColSize) );
      end if;
    end loop;
  end loop;
end;

Je suis surpris de voir combien il était difficile de trouver l'ordre de dépendance des tables en fonction des relations clés étrangères. Je avais besoin parce que je voulais supprimer les données de toutes les tables et l'importer à nouveau. Voici la requête que j'ai écrit à la liste des tables dans l'ordre de dépendance. J'ai pu les scripts à l'aide de la requête supprime ci-dessous, et à l'importation en utilisant à nouveau 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;

contraintes Oracle utilise les index de table pour référencer les données.
Pour en savoir quelles sont les tables font référence à une table, il suffit de regarder pour 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 .: références Désactivation améliore considérablement le temps de commandes DML (mise à jour, supprimer et insérer).

Cela peut aider beaucoup dans les opérations en vrac, 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;
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top