CASCADE SİLİNDİ


200

Birkaç basamaklı silme yapmak istediğim bir Postgresql veritabanı var. Ancak, tablolar ON DELETE CASCADE kuralıyla ayarlanmamıştır. Ben bir silme gerçekleştirmek ve Postgresql sadece bu kez basamakla söylemek için herhangi bir yolu var mı? Eşdeğer bir şey

DELETE FROM some_table CASCADE;

Bu eski sorunun cevabı, böyle bir çözüm bulunmadığını gösteriyor, ancak bu soruyu açıkça emin olmak istediğimi düşündüm.


Lütfen aşağıdaki özel işlevime bakın. Bazı kısıtlamalarla mümkündür.
Joe Love

Yanıtlar:


175

Hayır. Bunu yalnızca bir kez yapmak için, basamaklandırmak istediğiniz tablo için delete deyimini yazmanız yeterlidir.

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

12
Orijinal basamaklı (özyineleme) basamaklı başka yabancı anahtarlar olabilir, çünkü bu mutlaka işe yaramaz. Hatta tablo a'nın b'ye atıfta bulunduğu bir döngüye girebilirsiniz. Bunu genel anlamda başarmak için aşağıdaki tabloma bakın, ancak bazı kısıtlamaları vardır. Basit bir tablo kurulumunuz varsa yukarıdaki kodu deneyin, ne yaptığınızı anlamak daha kolaydır.
Joe Love

2
Basit, güvenli. Yoğunluk ekleriniz varsa, bunları tek bir işlemde çalıştırmalısınız.
İsmail Yavuz

41

Eğer gerçekten istiyorsan DELETE FROM some_table CASCADE; yani " tablodan tüm satırları kaldırmaksome_table kullanabilirsiniz," TRUNCATEyerine DELETEve CASCADEher zaman desteklenir. Ancak, bir silme ile seçici silme kullanmak istiyorsanız where, TRUNCATEyeterince iyi değildir.

BAKIMLA KULLAN - Yabancı anahtar kısıtlaması olan tüm tabloların tüm satırlarınısome_table ve bu tablolarda vb. Kısıtlamaları olan tüm tabloları kaldıracaktır .

Postgres destekleri CASCADEile KESILMESINDEN komutu :

TRUNCATE some_table CASCADE;

Diğer eşzamanlı işlemlerden tam olarak izole edilmemesine ve başka uyarılara sahip olmasına rağmen, bu işlemseldir (yani geri alınabilir). Ayrıntılar için belgeleri okuyun.


226
açıkça "birkaç basamaklı siler" ≠ tablodan tüm verileri bırakarak…
lensovet

33
Bu, some_table üzerinde yabancı anahtar kısıtlaması olan tüm tabloların tüm satırlarını ve bu tablolarda vb. Kısıtlamaları olan tüm tabloları bırakacaktır ... bu çok tehlikelidir.
AJP

56
dikkat. bu pervasız bir cevap.
Jordan Arseno

4
Birisi bu cevabı silmek için işaretledi - muhtemelen buna katılmadıkları için. Bu durumda doğru eylem şekli bayrak değil, aşağı oy etmektir.
Wai Ha Lee

7
Üstte bir uyarı var. Bunu görmezden gelmeyi seçerseniz, kimse size yardımcı olamaz. "CopyPaste" kullanıcılarınızın burada gerçek tehlike olduğunu düşünüyorum.
BluE

28

Birincil anahtarına göre herhangi bir satırı silmek için (özyinelemeli) bir işlev yazdım. Bunu yazdım çünkü kısıtlamalarımı "silme kaskatında" olarak oluşturmak istemedim. Karmaşık veri kümelerini (DBA olarak) silmek istedim, ancak programcılarımın tüm yansımaları düşünmeden silme işlemine izin vermesini istemedim. Bu işlevi hala test ediyorum, bu yüzden hatalar olabilir - ama DB'nizde çok sütunlu birincil (ve dolayısıyla yabancı) anahtarlar varsa lütfen denemeyin. Ayrıca, anahtarların tümü dize biçiminde temsil edilebilmelidir, ancak bu kısıtlamaya sahip olmayan bir şekilde yazılabilir. Ben zaten ÇOK AKILLI bu işlevi kullanın, ben her şeyi basamaklı kısıtlamaları etkinleştirmek için verilerime çok değer. Temel olarak bu işlev şema, tablo adı ve birincil değerde (dize biçiminde) geçirilir, ve o tabloda herhangi bir yabancı anahtar bularak başlayacak ve verinin mevcut olmadığından emin olacaktır - eğer varsa, bulunan veriler üzerinde kendini tekrar tekrar çağırır. Sonsuz döngüleri önlemek için zaten silinmek üzere işaretlenmiş bir veri dizisi kullanır. Lütfen test edin ve sizin için nasıl çalıştığını bana bildirin. Not: Biraz yavaş. Ben şöyle diyorum: 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;

Özellikle kendinden referans tablolarıyla her zaman olur. Farklı departmanlarda farklı yönetim katmanlarına sahip bir şirket veya genel bir hiyerarşik sınıflandırma düşünün. Evet, bu fonksiyonun dilimlenmiş ekmekten beri mutlak en iyi şey olmadığını kabul ediyorum, ancak doğru durumda yararlı bir araç.
Joe Love

Yeniden yazdığınızda, kimlik dizisini kabul ederseniz ve aynı zamanda ( INseçili =küme mantığını kullanma adımı) yerine alt seçimli operatörü kullanacak sorgular oluşturursanız çok daha hızlı olur.
Hubbitus

2
Çözümünüz için teşekkür ederim. Bazı testler yazdım ve bir kaydı silmem gerekiyordu ve bu silme işlemini basamaklamakta zorlanıyordum. İşleviniz gerçekten iyi çalıştı!
Fernando Camargo

1
@JoeLove hangi hız sorununuz var? Bu durumda özyineleme aklımda tek doğru çözümdür.
Hubbitus

1
@ arthur bunu yapmak için muhtemelen satır -> json -> metnin bazı sürümünü kullanabilirsiniz, ancak, o kadar ileri gitmedim. Yıllar boyunca tek bir birincil anahtarın (potansiyel ikincil anahtarlarla) birçok nedenden dolayı iyi olduğunu gördüm.
Joe Love

17

Doğru anlarsam, yabancı anahtar kısıtlamasını bırakarak, yeni bir tane ekleyerek (kademeli olarak), eşyalarınızı yaparak ve kısıtlayıcı yabancı anahtar kısıtlamasını yeniden oluşturarak istediğinizi yapabilmeniz gerekir.

Örneğin:

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

Tabii ki, zihinsel sağlığınız uğruna böyle bir şeyi bir prosedüre soyutlamalısınız.


4
Yabancı anahtarın veritabanını tutarsız hale getiren şeylerin yapılmasını engelleyebileceği varsayımıyla, bu başa çıkmanın yolu değildir. Şimdi "kötü" girişi silebilirsiniz ama gelecekte sorunlara neden olabilecek çok sayıda zombi parçası bırakıyorsunuz
Sprinterfreak

1
Tam olarak ne demek istiyorsun? kayıtlar basamaklı olarak silinecek tutarsızlık olmamalıdır.
Pedro Borges

1
"kötü parçalar" hakkında endişelenmektense (basamaklı kısıtlamalar hala tutarlı olacaktır), basamaklı kayıtların yeterince ileri gitmemesi konusunda DAHA FAZLA endişelenirim - silinen kayıtlar daha fazla silinmiş kayıt gerektiriyorsa, bu kısıtlamaların değiştirilmesi gerekecektir basamaklı sağlamak için. (veya bu senaryodan kaçınmak için yukarıda yazdığım işlevi kullanın) ... Her durumda son bir öneri: Bir işlem kullanın, böylece ters giderse geri alabilirsiniz.
Joe Love

7

Palehorse'un cevabına yorum yapamam, bu yüzden kendi cevabımı ekledim. Palehorse'ın mantığı iyi, ancak büyük veri kümelerinde verimlilik kötü olabilir.

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;

Sütunlarda dizinleriniz varsa ve veri kümesi birkaç kayıttan daha büyükse daha hızlıdır.


7

Evet, diğerlerinin söylediği gibi, uygun bir 'my_table ... CASCADE' (veya eşdeğerinden) SİLİN yok. Basamaklı olmayan yabancı anahtar korumalı çocuk kayıtlarını ve bunlarla ilgili atalarını silmek için seçenekleriniz şunları içerir:

  • Tüm silme işlemlerini alt tablolardan başlayarak tek tek bir sorguda açıkça gerçekleştirin (dairesel referanslarınız varsa bu uçmaz); veya
  • Tüm silme işlemlerini tek bir (potansiyel olarak büyük) sorguda açıkça gerçekleştirin; veya
  • Basamaklı olmayan yabancı anahtar kısıtlamalarınızın 'ON DELETE NO ACTION DEFERRABLE' olarak oluşturulduğunu varsayarsak, tüm silme işlemlerini tek bir işlemde açıkça gerçekleştirin; veya
  • Geçici olarak 'eylem yok' ve 'yabancı anahtar kısıtlamalarını' grafikte geçici olarak bırakın, bunları CASCADE olarak yeniden oluşturun, rahatsız edici ataları silin, yabancı anahtar kısıtlamalarını tekrar bırakın ve son olarak orijinal oldukları gibi yeniden oluşturun (böylece geçici olarak bütünlüğünü zayıflatır) verileriniz); veya
  • Muhtemelen eğlenceli bir şey.

Yabancı anahtar kısıtlamalarını atlatmanın uygun olmadığını bilerek, sanırım; ancak neden belirli durumlarda bunu yapmak istediğinizi anlıyorum. Bazı sıklıkta yapacağınız bir şeyse ve DBA'ların bilgeliğini her yere yaymaya hazırsanız, bir prosedürle otomatikleştirmek isteyebilirsiniz.

Buraya birkaç ay önce "CASCADE DELETE Yalnızca bir kez" sorusuna cevap aramaya geldim (aslen on yıl önce soruldu!). Joe Love'ın zeki çözümünden (ve Thomas CG de Vilhena'nın varyantından) biraz kilometre aldım, ama sonunda kullanım durumumun beni farklı bir yaklaşım benimsemeye zorlayan özel gereksinimleri (bir tane için tablo içi dairesel referansların ele alınması) vardı. Bu yaklaşım nihayetinde özyinelemeli hale geldi (PG 10.10).

Bir süredir üretimde özyinelemeli_delete kullanıyorum ve nihayetinde (fikir) burada fikir arayan başkaları için kullanılabilir hale getirecek kadar emin hissediyorum. Joe Love'ın çözümünde olduğu gibi, veritabanınızdaki tüm yabancı anahtar kısıtlamaları anlık olarak CASCADE olarak ayarlanmış gibi tüm veri grafiklerini silmenize izin verir, ancak birkaç ek özellik sunar:

  • Silme hedefinin ve bağımlı grafiklerinin ASCII önizlemesini sağlar.
  • Özyinelemeli CTE'leri kullanarak tek bir sorguda silme gerçekleştirir.
  • Dairesel bağımlılıkları, tablo içi ve tablolar arası işler.
  • Kompozit tuşları yönetir.
  • 'Varsayılan olarak ayarla' ve 'null ayarla' kısıtlamalarını atlar.

Bir hata alıyorum: HATA: dizi öğe sayısı çift olmalıdır Nerede: PL / pgSQL işlevi _recursively_delete (regclass, metin [], tamsayı, jsonb, tamsayı, metin [], jsonb, jsonb) atama SQL deyiminde satır 15 SQL deyimi de PL / pgSQL fonksiyonu recursively_delete (regclass, anyelement, boolean) hattını 73 "(ARG_table, VAR_pk_col_names) _recursively_delete SELECT * FROM"
Joe Aşk

Hey, @ JoeLove. Denediğiniz için teşekkürler. Bana üreme adımları verebilir misiniz? Peki PG sürümünüz nedir?
TRL

Bunun yardımcı olacağından emin değilim. ama ben sadece fonksiyonlarını yarattım ve sonra aşağıdaki kodu koştum: select recursively_delete ('dallas.vendor', 1094, false) Bazı hata ayıklamalarından sonra, bunun yarasadan öldüğünü görüyorum - yani, ilk çağrı gibi görünüyor işlev yerine, birden fazla şey yaptıktan sonra değil. Referans için PG 10.8
Joe Love

@JoeLove, Lütfen trl-fix-array_must_have_even_number_of_element ( github.com/trlorenz/PG-recursively_delete/pull/2 ) öğesini deneyin .
TRL

O dalı denedim ve orijinal hatayı düzeltti. Ne yazık ki, orijinal sürümümden daha hızlı değil (ilk etapta bunu yazma noktanız olmayabilir). "Silme basamaklı" ile yinelenen yabancı anahtarlar oluşturur, daha sonra orijinal kayıt silme, sonra yeni oluşturulan tüm yabancı anahtarları bırakarak başka bir girişim üzerinde çalışıyorum,
Joe Love

3

Bunu otomatikleştirmek için kullanabilirsiniz, yabancı anahtar kısıtlamasını tanımlayabilirsiniz ON DELETE CASCADE. Yabancı anahtar kısıtlamalarının el kitabını
alıntılarım :

CASCADE atıfta bulunulan bir satır silindiğinde, onu referans alan satırların da otomatik olarak silinmesi gerektiğini belirtir.


1
Bu OP'yi ele almasa da, yabancı anahtarlı satırların ne zaman silinmesi gerektiği için iyi bir planlamadır. Ben Franklin'in dediği gibi, "bir ons önleme bir kiloluk tedaviye değer."
Jesuisme

1
Uygulamanız çok sayıda kardeş içeren bir kaydı silerse ve küçük bir hata yerine, büyük bir veri kümesini kalıcı olarak sildiyseniz, bu çözümün oldukça tehlikeli olabileceğini keşfettim.
Joe Love

2

Joe Love'ın cevabını aldım ve işlevi daha hızlı hale getirmek INyerine alt =seçimli operatör kullanarak yeniden yazdım (Hubbitus'un önerisine göre):

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;

2
Bu konuya bakıp kendi kendini referanslama kısıtlamaları ve benzerleriyle ne kadar iyi çalıştığını görmem gerekecek. Benzer bir şey yapmaya çalıştım ama tam olarak çalışmasını sağlayamadım. Çözümünüz benim için işe yararsa, onu uygulayacağım. Bu, paketlenmeli ve github veya başka bir şeye konması gereken birçok dba aracından biridir.
Joe Love

Çok kiracılı bir CMS için orta boyutlu veritabanlarım var (istemcilerin hepsi aynı tabloları paylaşıyor). Benim sürümüm ("in" ile) eski bir istemcinin tüm izlerini silmek için oldukça yavaş çalışıyor gibi görünüyor ... Ben hızları karşılaştırmak için bazı mockup verileri ile denemek ilgileniyorum. Kullanım durumunuzda fark ettiğiniz hız farkı hakkında söyleyebileceğiniz bir şey var mı?
Joe Love

Kullanım durumum için, inoperatörü ve alt sorguları kullanırken 10x hızında bir hız fark ettim .
Thomas CG de Vilhena

1

Basamaklama seçeneğiyle silme yalnızca yabancı anahtarların tanımlandığı tablolara uygulanır. Bir silme işlemi yaparsanız ve yabancı anahtar kısıtlamasını ihlal edeceği için bunu yapamayacağınızı söylüyorsa, basamaklı dizge, rahatsız edici satırları silmesine neden olur.

İlişkili satırları bu şekilde silmek istiyorsanız, önce yabancı anahtarları tanımlamanız gerekir. Ayrıca, açıkça bir işleme başlamasını istemediğiniz veya varsayılanları değiştirmediğiniz sürece, temizliği çok zaman alan bir otomatik taahhüt gerçekleştireceğini unutmayın.


2
Grant'in cevabı kısmen yanlış - Postgresql, DELETE sorgularında CASCADE'i desteklemiyor. postgresql.org/docs/8.4/static/dml-delete.html
Fredrik Wendt

Silme sorgusunda neden desteklenmediğine dair bir fikriniz var mı?
Teifion

2
buna göre ayarlanmamış bir tablo üzerinde "kademeli olarak silme" nin bir yolu yoktur, yani yabancı anahtar kısıtlaması sorunun asıl amacı bu olan ON DELACE CASCADE olarak tanımlanmamıştır.
lensovet

Bir cevap olarak bu soruya, bu tamamen yanlıştır. Bir kez CASCADE'in bir yolu yoktur.
Jeremy
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.