database - tablas - identity oracle 11g
¿Cómo encontrar dependencias de clave externa que apuntan a un registro en Oracle? (6)
Podemos usar el diccionario de datos para identificar las tablas que hacen referencia a la clave principal de la tabla en cuestión. De eso podemos generar algunos SQL dinámicos para consultar esas tablas para el valor que queremos borrar:
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>
Este ejemplo hace trampa 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 solución de estos problemas se deja como un ejercicio para el lector;)
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.
Tengo las herramientas SQL Developer de Oracle y PL / SQL Developer de AllRoundAutomations a mi disposición.
¡Gracias por adelantado!
Siempre miro las claves foráneas para la mesa de inicio y vuelvo a trabajar. Las herramientas de DB generalmente tienen un nodo de dependencias o restricciones. Sé que L / SQL Developer tiene una forma de ver los FK, pero ha pasado un tiempo desde que lo usé, así que no puedo explicarlo ...
simplemente reemplace 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
Recientemente tuve un problema similar, pero experimenté pronto, que encontrar las dependencias directas no es suficiente. Así que escribí una consulta para mostrar un árbol de dependencias de clave externa multinivel:
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'';
Da un resultado como este cuando se usa SHIPMENT como MYTABLE 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
...
Me sorprendió lo difícil que era encontrar el orden de dependencia de las tablas en función de las relaciones de claves externas. 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;
Las restricciones de Oracle usan índices de tabla para los 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 .: la desactivación de referencias mejora considerablemente el tiempo de los comandos DML (actualizar, eliminar e insertar).
Esto puede ayudar mucho en 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;
Aquí está mi solución para enumerar 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 qué columna de qué tabla está haciendo referencia a qué columna de su tabla de destino (y puede filtrarla).