2010-03-24 16 views
17

Ho un database Oracle molto grande, con molte tabelle e milioni di righe. Devo cancellare uno di questi, ma voglio assicurarmi che il rilascio non interromperà altre righe dipendenti che puntano ad esso come un record di chiave esterna. C'è un modo per ottenere una lista di tutti gli altri record, o almeno schemi di tabelle, che puntano a questa riga? So che potrei semplicemente provare a cancellarlo da solo, e prendere l'eccezione, ma io non eseguirò il copione da solo e ne avrò bisogno per funzionare in modo pulito la prima volta.Come trovare le dipendenze di chiavi esterne che puntano a un record in Oracle?

Ho gli strumenti SQL Developer di Oracle e PL/SQL Developer di AllRoundAutomations a mia disposizione.

Grazie in anticipo!

+0

non tenterà di eliminare l'eccezione di lancio del record di riferimento? – Andrey

+0

@Andrey sì, ma ho bisogno che funzioni senza lanciare un'eccezione. – daveslab

risposta

23

Guardo sempre le chiavi esterne per il tavolo di partenza e lavoro indietro. Gli strumenti DB di solito hanno un nodo di dipendenze o vincoli. So L/SQL Developer ha un modo di vedere FK di, ma è stato un po 'che ho usato, quindi non posso spiegarlo ...

basta sostituire XXXXXXXXXXXX con un nome di tabella ...

/* 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 
+1

Sql fa distinzione tra maiuscole e minuscole – eschneider

+0

Potresti mettere quella query in modalità 'code', quindi è un po 'più facile da leggere? Altrimenti, ci sto provando ora. – daveslab

+0

Selezionare la parte della risposta con SQL e quindi premere o premere il pulsante direttamente a destra nella barra degli strumenti dell'editor con le virgolette come simbolo. Assicurati di suddividerlo su più righe. – daveslab

3

Possiamo utilizzare il dizionario dati per identificare le tabelle che fanno riferimento alla chiave primaria della tabella in questione. Da ciò possiamo generare alcuni SQL dinamici per interrogare tali tabelle per il valore che vogliamo zappare:

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> 

Questo esempio imbroglia un po '. Il nome della chiave primaria di destinazione è hardcoded e la colonna di riferimento ha lo stesso nome su tutte le tabelle dipendenti. Risoluzione di questi problemi è lasciata come esercizio per il lettore;)

23

Ecco la mia soluzione per elencare tutti i riferimenti a un tavolo:

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' 
; 

Con questa soluzione si ha anche l'informazione di quale colonna di quale tabella fa riferimento a quale colonna della tabella di destinazione (e puoi filtrarla).

5

Ho avuto un problema simile di recente, ma ho riscontrato subito che trovare le dipendenze dirette non è sufficiente. Così ho scritto una query per mostrare un albero di multilivello dipendenze chiave esterna:

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'; 

Dà un risultato come questo, quando si utilizza spedizione come MYTABLE nel mio database:

SHIPMENT <-- ADDRESS 
SHIPMENT <-- PACKING_LIST 
    PACKING_LIST <-- PACKING_LIST_DETAILS 
    PACKING_LIST <-- PACKING_UNIT 
     PACKING_UNIT <-- PACKING_LIST_ITEM 
    PACKING_LIST <-- PO_PACKING_LIST 
... 
0

Sono rimasto sorpreso da quanto difficile era trovare l'ordine di dipendenza delle tabelle basato su relazioni con le chiavi esterne. Ne avevo bisogno perché volevo eliminare i dati da tutte le tabelle e importarlo di nuovo. Ecco la query che ho scritto per elencare le tabelle in ordine di dipendenza. Sono stato in grado di eseguire lo script delle eliminazioni utilizzando la query riportata di seguito e importare nuovamente utilizzando i risultati della query in ordine inverso.

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

I vincoli Oracle utilizzano gli indici di tabella per fare riferimento ai dati.
Per scoprire quali tabelle fanno riferimento a una tabella, è sufficiente cercare l'indice in ordine inverso.

/* 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 .: riferimenti Disattivazione migliora notevolmente il tempo di DML comandi (aggiornare, cancellare e inserire).

Questo può aiutare molto in operazioni di massa, dove si sa che tutti i dati sono coerenti.

Problemi correlati