Numa base Oracle 11g, foi identificado um furo em uma regra de negocio que consequentemente afetou a aplicacao e o banco de dados. Ocorreu que uma informacao pode ser gravada duas vezes, ou seja, ficou duplicada em termos de negocio.Identificado o problema, o cliente pediu uma query para varrer todos os registros que referenciavam de alguma forma aquele ID especifico.O agravante era que o sistema ja estava em producao e em momento nenhum poderia sair do ar.Achei que alterar as constraints para conseguir um 'Delete Cascade' seria a solucao = FAIL.Entao, analisei bem a situacao e o padrao de nomenclatura dos objetos deste banco. Como existia um padrao bem definido, carreguei em tabelas temporarias as informacoes que precisava e depois sai montando as queries. Vamos la:Tudo comecava em um Projeto (TB_PROJETO). Entao no primeiro nivel carreguei as tabelas e campos pai e tabelas e campos filhos:-- CARREGAR NIVEL 1 = pai + filhosCREATE GLOBAL TEMPORARY TABLE TB_ADM_NIVEL_1 ASSELECT REPLACE(R_CONSTRAINT_NAME,'PK_', '') AS TABELA_PAI_0, REPLACE(R_CONSTRAINT_NAME AS CAMPO_PAI_0, 'PK_', 'ID_') AS CAMPO_PAI_0, TABLE_NAME AS TABELA_FILHA_1, REPLACE(TABLE_NAME, 'TB', 'ID') AS CAMPO_FILHA_1FROM ALL_CONSTRAINTS WHERE OWNER = 'O-W-N-E-R' AND CONSTRAINT_TYPE = 'R' AND replace(R_CONSTRAINT_NAME,'PK_', '') = 'TB_PROJETO' ORDER BY TABLE_NAME;No segundo nivel carreguei as tabelas e campos que sao filhas no nivel 1 e agora pai no nivel 2:-- CARREGAR NIVEL 2 = filhos + netosCREATE GLOBAL TEMPORARY TABLE TB_ADM_NIVEL_2 ASSELECT REPLACE(R_CONSTRAINT_NAME,'PK_', '') AS TABELA_PAI_1, REPLACE(R_CONSTRAINT_NAME, 'PK_TB', 'ID' AS CAMPO_PAI_1, TABLE_NAME AS TABELA_FILHA_2, REPLACE(TABLE_NAME, 'TB', 'ID') AS CAMPO_FILHA_2FROM ALL_CONSTRAINTS WHERE OWNER = 'O-W-N-E-R' AND CONSTRAINT_TYPE = 'R' AND replace(R_CONSTRAINT_NAME,'PK_', '') IN (SELECT TABELA_FILHA_1 FROM TB_ADM_NIVEL_1 WHERE TABELA_FILHA_1 <> TABELA_PAI_0);-- CARREGAR NIVEL 3 = netos + bisnetosCREATE GLOBAL TEMPORARY TABLE TB_ADM_NIVEL_3 ASSELECT REPLACE(R_CONSTRAINT_NAME,'PK_', '') AS TABELA_PAI_2, REPLACE(R_CONSTRAINT_NAME, 'PK_TB', 'ID') AS CAMPO_PAI_2, TABLE_NAME AS TABELA_FILHA_3, REPLACE(TABLE_NAME, 'TB', 'ID') AS CAMPO_FILHA_3FROM ALL_CONSTRAINTS WHERE OWNER = 'O-W-N-E-R' AND CONSTRAINT_TYPE = 'R' AND replace(R_CONSTRAINT_NAME,'PK_', '') IN (SELECT TABELA_FILHA_2 FROM TB_ADM_NIVEL_2 WHERE TABELA_FILHA_2 <> TABELA_PAI_1);-- CARREGAR NIVEL 4 = bisnetos + tataranetosCREATE GLOBAL TEMPORARY TABLE TB_ADM_NIVEL_4 ASSELECT REPLACE(R_CONSTRAINT_NAME,'PK_', '') AS TABELA_PAI_3, REPLACE(R_CONSTRAINT_NAME, 'PK_TB', 'ID') AS CAMPO_PAI_3, TABLE_NAME AS TABELA_FILHA_4, REPLACE(TABLE_NAME, 'TB', 'ID') AS CAMPO_FILHA_4FROM ALL_CONSTRAINTS WHERE OWNER = 'O-W-N-E-R' AND CONSTRAINT_TYPE = 'R' AND replace(R_CONSTRAINT_NAME,'PK_', '') IN (SELECT TABELA_FILHA_3 FROM TB_ADM_NIVEL_3 WHERE TABELA_FILHA_3 <> TABELA_PAI_2);-- CARREGAR NIVEL 5 = VAZIOCREATE GLOBAL TEMPORARY TABLE TB_ADM_NIVEL_5 ASSELECT REPLACE(R_CONSTRAINT_NAME,'PK_', '') AS TABELA_PAI_4, REPLACE(R_CONSTRAINT_NAME, 'PK_TB', 'ID') AS CAMPO_PAI_4, TABLE_NAME AS TABELA_FILHA_5, REPLACE(TABLE_NAME, 'TB', 'ID') AS CAMPO_FILHA_5FROM ALL_CONSTRAINTS WHERE OWNER = 'O-W-N-E-R' AND CONSTRAINT_TYPE = 'R' AND replace(R_CONSTRAINT_NAME,'PK_', '') IN (SELECT TABELA_FILHA_4 FROM TB_ADM_NIVEL_4 WHERE TABELA_FILHA_4 <> TABELA_PAI_3);Criei tabelas ate o nivel em que nao existia mais registros no ultimo nivel. No caso, a tabela TB_ADM_NIVEL_5 nao possui nenhum registro e portanto pode ser dropada. Trabalhei apenas com as tabelas com informacoes de nivel 0 a 4.DROP TABLE TB_ADM_NIVEL_5;Assim, recursivamente, da tabela 4 ate a 1, usei uma query para carregar uma tabela com a ordem dos registros nas tabelas que deveriam ser apagados. Uni cada query pelo comando UNION para que ele fizesse a eliminacao de possiveis linhas repetidas. Definir estas querys, foi a parte mais delicada da solucao pois exigiu um entendimento quanto a logica dos relacionamentos