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!
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
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).
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
...
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;)
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;
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;