سؤال

لدي قاعدة بيانات الإنترنت التي أريد القيام به عدد قليل من تتالي حذف.إلا أن الجداول لا مع على تتالي حذف القاعدة.هل هناك أي طريقة يمكن أن تؤدي إلى حذف ونقول كيو تتالي فقط هذا مرة واحدة ؟ شيء ما يعادل

DELETE FROM some_table CASCADE;

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

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

المحلول

لا.أن تفعل ذلك مرة واحدة فقط يمكنك ببساطة كتابة حذف بيان الجدول الذي تريد تتالي.

DELETE FROM some_child_table WHERE some_fk_field IN (SELECT some_id FROM some_Table);
DELETE FROM some_table;

نصائح أخرى

إذا كنت تريد حقا DELETE FROM some_table CASCADE; وهو ما يعني "إزالة كافة الصفوف من الجدول some_table"يمكنك استخدام TRUNCATE بدلا من DELETE و CASCADE دائما معتمدة.ومع ذلك ، إذا كنت ترغب في استخدام انتقائية حذف مع where الشرط ، TRUNCATE ليست جيدة بما فيه الكفاية.

استخدم بحذر - هذا سوف إسقاط جميع الصفوف من جميع الجداول التي لها قيد المفتاح الخارجي على some_table و جميع الجداول التي تحتوي على القيود المفروضة على تلك الجداول ، إلخ.

بوستجرس يدعم CASCADE مع اقتطاع الأمر:

TRUNCATE some_table CASCADE;

بسهولة هذه المعاملات (أيويمكن إرجاع الظهر) ، على الرغم من أنها ليست معزولة تماما عن غيرها من العمليات المتزامنة ، وله عدة محاذير أخرى.قراءة مستندات للحصول على التفاصيل.

كتبت (العودية) وظيفة حذف أي صف على المفتاح الأساسي.كتبت هذا لأنني لا أريد أن إنشاء القيود "على حذف شلال".أردت أن تكون قادرا على حذف مجموعات البيانات المعقدة (كما DBA) ولكن لا يسمح المبرمجين أن تكون قادرة على تتالي حذف دون التفكير من خلال كل من تداعيات.أنا لا تزال اختبار هذه الوظيفة ، لذلك قد يكون هناك أخطاء في ذلك-ولكن من فضلك لا تحاول ذلك إذا DB الخاص بك قد متعدد العمود الأساسي (وبالتالي الأجنبية) مفاتيح.أيضا, مفاتيح كل شيء يجب أن تكون قادرة على أن تكون ممثلة في شكل سلسلة ، ولكن يمكن أن تكون مكتوبة بطريقة أن لا يكون هذا التقييد.يمكنني استخدام هذه الوظيفة بشكل مقتصد جدا على أي حال ، لقد قيمة البيانات الخاصة بي أكثر من اللازم لتمكين المتتالية القيود على كل شيء.أساسا هذا هو وظيفة مرت في المخطط اسم الجدول و قيمة أساسية (في شكل سلسلة) ، وسوف تبدأ من خلال إيجاد أي مفاتيح خارجية في هذا الجدول ويتأكد من البيانات لا وجود لها-إذا كان كذلك ، فإنه يدعو بشكل متكرر itsself على البيانات الموجودة.فإنه يستخدم مجموعة من البيانات بالفعل وضع علامة عليها للحذف لمنع حلقات لانهائية.يرجى اختبار بها واسمحوا لي أن أعرف كيف يعمل لك.ملاحظة:انها بطيئة بعض الشيء.أنا أسميها هكذا:select delete_cascade('public','my_table','1');

create or replace function delete_cascade(p_schema varchar, p_table varchar, p_key varchar, p_recursion varchar[] default null)
 returns integer as $$
declare
    rx record;
    rd record;
    v_sql varchar;
    v_recursion_key varchar;
    recnum integer;
    v_primary_key varchar;
    v_rows integer;
begin
    recnum := 0;
    select ccu.column_name into v_primary_key
        from
        information_schema.table_constraints  tc
        join information_schema.constraint_column_usage AS ccu ON ccu.constraint_name = tc.constraint_name and ccu.constraint_schema=tc.constraint_schema
        and tc.constraint_type='PRIMARY KEY'
        and tc.table_name=p_table
        and tc.table_schema=p_schema;

    for rx in (
        select kcu.table_name as foreign_table_name, 
        kcu.column_name as foreign_column_name, 
        kcu.table_schema foreign_table_schema,
        kcu2.column_name as foreign_table_primary_key
        from information_schema.constraint_column_usage ccu
        join information_schema.table_constraints tc on tc.constraint_name=ccu.constraint_name and tc.constraint_catalog=ccu.constraint_catalog and ccu.constraint_schema=ccu.constraint_schema 
        join information_schema.key_column_usage kcu on kcu.constraint_name=ccu.constraint_name and kcu.constraint_catalog=ccu.constraint_catalog and kcu.constraint_schema=ccu.constraint_schema
        join information_schema.table_constraints tc2 on tc2.table_name=kcu.table_name and tc2.table_schema=kcu.table_schema
        join information_schema.key_column_usage kcu2 on kcu2.constraint_name=tc2.constraint_name and kcu2.constraint_catalog=tc2.constraint_catalog and kcu2.constraint_schema=tc2.constraint_schema
        where ccu.table_name=p_table  and ccu.table_schema=p_schema
        and TC.CONSTRAINT_TYPE='FOREIGN KEY'
        and tc2.constraint_type='PRIMARY KEY'
)
    loop
        v_sql := 'select '||rx.foreign_table_primary_key||' as key from '||rx.foreign_table_schema||'.'||rx.foreign_table_name||'
            where '||rx.foreign_column_name||'='||quote_literal(p_key)||' for update';
        --raise notice '%',v_sql;
        --found a foreign key, now find the primary keys for any data that exists in any of those tables.
        for rd in execute v_sql
        loop
            v_recursion_key=rx.foreign_table_schema||'.'||rx.foreign_table_name||'.'||rx.foreign_column_name||'='||rd.key;
            if (v_recursion_key = any (p_recursion)) then
                --raise notice 'Avoiding infinite loop';
            else
                --raise notice 'Recursing to %,%',rx.foreign_table_name, rd.key;
                recnum:= recnum +delete_cascade(rx.foreign_table_schema::varchar, rx.foreign_table_name::varchar, rd.key::varchar, p_recursion||v_recursion_key);
            end if;
        end loop;
    end loop;
    begin
    --actually delete original record.
    v_sql := 'delete from '||p_schema||'.'||p_table||' where '||v_primary_key||'='||quote_literal(p_key);
    execute v_sql;
    get diagnostics v_rows= row_count;
    --raise notice 'Deleting %.% %=%',p_schema,p_table,v_primary_key,p_key;
    recnum:= recnum +v_rows;
    exception when others then recnum=0;
    end;

    return recnum;
end;
$$
language PLPGSQL;

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

على سبيل المثال:

testing=# create table a (id integer primary key);
NOTICE:  CREATE TABLE / PRIMARY KEY will create implicit index "a_pkey" for table "a"
CREATE TABLE
testing=# create table b (id integer references a);
CREATE TABLE

-- put some data in the table
testing=# insert into a values(1);
INSERT 0 1
testing=# insert into a values(2);
INSERT 0 1
testing=# insert into b values(2);
INSERT 0 1
testing=# insert into b values(1);
INSERT 0 1

-- restricting works
testing=# delete from a where id=1;
ERROR:  update or delete on table "a" violates foreign key constraint "b_id_fkey" on table "b"
DETAIL:  Key (id)=(1) is still referenced from table "b".

-- find the name of the constraint
testing=# \d b;
       Table "public.b"
 Column |  Type   | Modifiers 
--------+---------+-----------
 id     | integer | 
Foreign-key constraints:
    "b_id_fkey" FOREIGN KEY (id) REFERENCES a(id)

-- drop the constraint
testing=# alter table b drop constraint b_a_id_fkey;
ALTER TABLE

-- create a cascading one
testing=# alter table b add FOREIGN KEY (id) references a(id) on delete cascade; 
ALTER TABLE

testing=# delete from a where id=1;
DELETE 1
testing=# select * from a;
 id 
----
  2
(1 row)

testing=# select * from b;
 id 
----
  2
(1 row)

-- it works, do your stuff.
-- [stuff]

-- recreate the previous state
testing=# \d b;
       Table "public.b"
 Column |  Type   | Modifiers 
--------+---------+-----------
 id     | integer | 
Foreign-key constraints:
    "b_id_fkey" FOREIGN KEY (id) REFERENCES a(id) ON DELETE CASCADE

testing=# alter table b drop constraint b_id_fkey;
ALTER TABLE
testing=# alter table b add FOREIGN KEY (id) references a(id) on delete restrict; 
ALTER TABLE

بالطبع يجب مجردة مثل هذه الاشياء في الإجراء من أجل الصحة العقلية الخاصة بك.

لا أستطيع التعليق palehorse الإجابة لذلك أضفت بلدي الإجابة.Palehorse logick على ما يرام ولكن الكفاءة يمكن أن تكون سيئة مع مجموعات البيانات الضخمة.

DELETE FROM some_child_table sct WHERE exists  (SELECT FROM some_Table st 
where sct.some_fk_fiel=st.some_id );
DELETE FROM some_table;

وهو أسرع إذا كان لديك مؤشرات على أعمدة مجموعة البيانات هو أكبر ثم قليل من السجلات.

يمكنك استخدام أتمتة هذا ، يمكنك تحديد قيد المفتاح الخارجي مع ON DELETE CASCADE.
لقد اقتبس دليل قيود المفاتيح الخارجية:

CASCADE وتنص على أنه عند المشار إليه الصف حذف صف(s) المراجع يجب أن تحذف تلقائيا أيضا.

أخذت جو الحب هو الجواب وأعاد ذلك باستخدام IN مشغل الفرعية يختار بدلا من = لجعل وظيفة بشكل أسرع (وفقا Hubbitus اقتراح):

create or replace function delete_cascade(p_schema varchar, p_table varchar, p_keys varchar, p_subquery varchar default null, p_foreign_keys varchar[] default array[]::varchar[])
 returns integer as $$
declare

    rx record;
    rd record;
    v_sql varchar;
    v_subquery varchar;
    v_primary_key varchar;
    v_foreign_key varchar;
    v_rows integer;
    recnum integer;

begin

    recnum := 0;
    select ccu.column_name into v_primary_key
        from
        information_schema.table_constraints  tc
        join information_schema.constraint_column_usage AS ccu ON ccu.constraint_name = tc.constraint_name and ccu.constraint_schema=tc.constraint_schema
        and tc.constraint_type='PRIMARY KEY'
        and tc.table_name=p_table
        and tc.table_schema=p_schema;

    for rx in (
        select kcu.table_name as foreign_table_name, 
        kcu.column_name as foreign_column_name, 
        kcu.table_schema foreign_table_schema,
        kcu2.column_name as foreign_table_primary_key
        from information_schema.constraint_column_usage ccu
        join information_schema.table_constraints tc on tc.constraint_name=ccu.constraint_name and tc.constraint_catalog=ccu.constraint_catalog and ccu.constraint_schema=ccu.constraint_schema 
        join information_schema.key_column_usage kcu on kcu.constraint_name=ccu.constraint_name and kcu.constraint_catalog=ccu.constraint_catalog and kcu.constraint_schema=ccu.constraint_schema
        join information_schema.table_constraints tc2 on tc2.table_name=kcu.table_name and tc2.table_schema=kcu.table_schema
        join information_schema.key_column_usage kcu2 on kcu2.constraint_name=tc2.constraint_name and kcu2.constraint_catalog=tc2.constraint_catalog and kcu2.constraint_schema=tc2.constraint_schema
        where ccu.table_name=p_table  and ccu.table_schema=p_schema
        and TC.CONSTRAINT_TYPE='FOREIGN KEY'
        and tc2.constraint_type='PRIMARY KEY'
)
    loop
        v_foreign_key := rx.foreign_table_schema||'.'||rx.foreign_table_name||'.'||rx.foreign_column_name;
        v_subquery := 'select "'||rx.foreign_table_primary_key||'" as key from '||rx.foreign_table_schema||'."'||rx.foreign_table_name||'"
             where "'||rx.foreign_column_name||'"in('||coalesce(p_keys, p_subquery)||') for update';
        if p_foreign_keys @> ARRAY[v_foreign_key] then
            --raise notice 'circular recursion detected';
        else
            p_foreign_keys := array_append(p_foreign_keys, v_foreign_key);
            recnum:= recnum + delete_cascade(rx.foreign_table_schema, rx.foreign_table_name, null, v_subquery, p_foreign_keys);
            p_foreign_keys := array_remove(p_foreign_keys, v_foreign_key);
        end if;
    end loop;

    begin
        if (coalesce(p_keys, p_subquery) <> '') then
            v_sql := 'delete from '||p_schema||'."'||p_table||'" where "'||v_primary_key||'"in('||coalesce(p_keys, p_subquery)||')';
            --raise notice '%',v_sql;
            execute v_sql;
            get diagnostics v_rows = row_count;
            recnum := recnum + v_rows;
        end if;
        exception when others then recnum=0;
    end;

    return recnum;

end;
$$
language PLPGSQL;

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

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

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top