كيف تجد تبعيات مفتاح أجنبية تشير إلى سجل واحد في أوراكل؟

StackOverflow https://stackoverflow.com/questions/2509512

سؤال

لدي قاعدة بيانات Oracle كبيرة جدًا ، مع العديد من الجداول والملايين من الصفوف. أحتاج إلى حذف أحدهم ، لكنني أريد التأكد من أن إسقاطه لن يكسر أي صفوف أخرى تعتمد عليها تشير إليها كسجل مفتاح خارجي. هل هناك طريقة للحصول على قائمة بجميع السجلات الأخرى ، أو على الأقل مخططات الجدول ، التي تشير إلى هذا الصف؟ أعلم أنه يمكنني فقط محاولة حذفه بنفسي ، والاستمتاع بالاستثناء ، لكنني لن أقوم بتشغيل البرنامج النصي بنفسي وأحتاج إلى تنظيفه في المرة الأولى.

لديّ الأدوات SQL Developer من Oracle ، ومطور PL/SQL من AllRoundautomations تحت تصرفي.

شكرا مقدما!

هل كانت مفيدة؟

المحلول

أنا دائما أنظر إلى المفاتيح الأجنبية لطاولة البداية وأعود في طريقي إلى العودة. عادة ما يكون لأدوات DB تبعيات أو عقدة قيود. أعلم أن مطور PL/SQL لديه طريقة لرؤية FK's ، ولكن لقد مر بعض الوقت منذ أن استخدمته ، لذلك لا يمكنني شرح ذلك ...

فقط استبدل xxxxxxxxxxxx باسم الجدول ...

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

نصائح أخرى

هنا هو الحل الخاص بي لسرد جميع الإشارات إلى جدول:

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

مع هذا الحل ، لديك أيضًا معلومات عن عمود الجدول الذي يشير إلى عمود الجدول المستهدف الخاص بك (ويمكنك التصفية عليه).

لقد واجهت مشكلة مماثلة مؤخرًا ، لكنني عانيت قريبًا ، أن العثور على التبعيات المباشرة لا يكفي. لذلك كتبت استعلامًا لإظهار شجرة من تبعيات المفاتيح الخارجية متعددة المستويات:

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

إنه يعطي نتيجة مثل هذا ، عند استخدام الشحنة كأصوات في قاعدة البيانات الخاصة بي:

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

يمكننا استخدام قاموس البيانات لتحديد الجداول التي تشير إلى المفتاح الأساسي للجدول المعني. من ذلك يمكننا إنشاء بعض SQL الديناميكي للاستعلام عن تلك الجداول للقيمة التي نريد أن نرغب فيها:

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>

هذا المثال يغش قليلا. تم ترميز اسم المفتاح الأساسي الهدف ، ويحتوي عمود المرجع على نفس الاسم على جميع الجداول التابعة. يتم ترك إصلاح هذه المشكلات كتمرين للقارئ ؛)

كان وضع مماثل. في حالتي ، كان لدي بعض السجلات التي انتهى بها الأمر مع نفس المعرف الذي يختلف فقط حسب الحالة. أراد التحقق من السجلات التابعة الموجودة لكل منها لمعرفة أي أسهل لحذف/تحديث

يطبع ما يلي جميع سجلات الأطفال التي تشير إلى السجل المحدد ، لكل طاولة طفل مع عدد لكل مجموعة السجل/السجل الرئيسي

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;

لقد فوجئت بمدى صعوبة العثور على ترتيب التبعية للجداول على أساس العلاقات الرئيسية الخارجية. كنت بحاجة إليها لأنني أردت حذف البيانات من جميع الجداول واستيرادها مرة أخرى. إليكم الاستعلام الذي كتبته لإدراج الجداول في ترتيب التبعية. تمكنت من السيناريو الحذف باستخدام الاستعلام أدناه ، واستيراد مرة أخرى باستخدام نتائج الاستعلام بترتيب عكسي.

   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;

تستخدم قيود Oracle فهارس الجدول للبيانات المرجعية.
لمعرفة الجداول التي تشير إلى جدول واحد ، فقط ابحث عن الفهرس بترتيب عكسي.

/* 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.: تعطيل المراجع يحسن إلى حد كبير وقت أوامر DML (التحديث ، حذف وإدراج).

يمكن أن يساعد هذا كثيرًا في العمليات السائبة ، حيث تعرف أن جميع البيانات متسقة.

/* 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;
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top