Comment trouver les dépendances clés étrangères pointant vers un enregistrement dans Oracle?
-
22-09-2019 - |
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!
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;