2010-03-24 15 views
17

Tengo una base de datos Oracle muy grande, con muchas tablas y millones de filas. Necesito eliminar uno de ellos, pero quiero asegurarme de que al eliminarlo no se rompa ninguna otra fila dependiente que lo señale como un registro de clave externa. ¿Hay alguna manera de obtener una lista de todos los otros registros, o al menos los esquemas de tabla, que apuntan a esta fila? Sé que podría intentar eliminarlo yo mismo y atrapar la excepción, pero no ejecutaré el script yo mismo y necesitaré que se ejecute limpio la primera vez.¿Cómo encontrar dependencias de clave externa que apuntan a un registro en Oracle?

Tengo las herramientas SQL Developer de Oracle, y PL/SQL Developer de AllRoundAutomations a mi disposición.

¡Gracias de antemano!

+0

no intentará eliminar la excepción de arrojo de registro referenciada? – Andrey

+0

@Andrey sí lo hará, pero necesito que funcione sin lanzar una excepción. – daveslab

Respuesta

23

Siempre miro las teclas foráneas para la tabla de inicio y vuelvo a trabajar. Las herramientas de DB generalmente tienen un nodo de dependencias o restricciones. Sé L/SQL Developer tiene una manera de ver FK, pero ha sido un tiempo desde que he usado, así que no puedo explicarlo ...

basta con sustituir XXXXXXXXXXXX con un nombre de tabla ...

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

el Sql distingue entre mayúsculas y minúsculas – eschneider

+0

¿Podría poner esa consulta en modo 'código', por lo que es un poco más fácil de leer? De lo contrario, lo estoy intentando ahora. – daveslab

+0

Seleccione la parte de su respuesta con el SQL y luego presione o presione el botón a la derecha en la barra de herramientas del editor con las comillas como su símbolo. Asegúrate de dividirlo en varias líneas también. – daveslab

3

Podemos utilizar el diccionario de datos para identificar las tablas que hacen referencia a la clave principal de la tabla en cuestión. Desde que podemos generar algo de SQL dinámico para consultar las tablas para el valor que queremos hacer zapping:

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> 

En este ejemplo se engaña un poco. El nombre de la clave primaria de destino está codificado, y la columna de referencia tiene el mismo nombre en todas las tablas dependientes. La fijación de estos problemas se deja como ejercicio para el lector;)

23

Aquí está mi solución para listar todas las referencias a una tabla:

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 esta solución también tiene la información de la que la columna de los cuales mesa hace referencia a qué columna de la tabla de destino (y puede filtrar).

5

Tuve un problema similar recientemente, pero experimenté pronto, que encontrar las dependencias directas no es suficiente. Así que escribí una consulta para mostrar un árbol de niveles múltiples dependencias de clave externa:

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

Se da un resultado como este, cuando se utiliza ENVÍO como MITABLA en mi base de datos:

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

Me sorprendió lo fue difícil encontrar el orden de dependencia de las tablas basado en relaciones de claves foráneas. Lo necesitaba porque quería eliminar los datos de todas las tablas e importarlas de nuevo. Aquí está la consulta que escribí para enumerar las tablas en orden de dependencia. Pude guiar las eliminaciones usando la consulta a continuación e importar de nuevo usando los resultados de la consulta en orden 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

limitaciones Oracle utiliza índices de tablas con datos de referencia.
Para averiguar qué tablas hacen referencia a una tabla, simplemente busque el índice en orden 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 .: Desactivación de referencias mejora considerablemente el tiempo de LMD comandos (actualizar, eliminar e insertar).

Esto puede ayudar mucho en las operaciones masivas, donde sabe que todos los datos son consistentes.

/* 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; 
Cuestiones relacionadas