HAPUS CASCADE sekali saja

200

Saya memiliki database Postgresql di mana saya ingin melakukan beberapa penghapusan cascading. Namun, tabel tidak diatur dengan aturan ON DELETE CASCADE. Apakah ada cara saya bisa melakukan delete dan memberi tahu Postgresql untuk melakukan cascade sekali ini saja? Sesuatu yang setara dengan

DELETE FROM some_table CASCADE;

Jawaban untuk pertanyaan yang lebih lama ini membuatnya sepertinya tidak ada solusi seperti itu, tetapi saya pikir saya akan menanyakan pertanyaan ini secara eksplisit hanya untuk memastikan.

Eli Courtwright
sumber
Silakan lihat fungsi khusus saya di bawah ini. Itu mungkin dengan batasan tertentu.
Joe Love

Jawaban:

175

Tidak. Untuk melakukannya hanya sekali, Anda cukup menulis pernyataan hapus untuk tabel yang ingin Anda juntai.

DELETE FROM some_child_table WHERE some_fk_field IN (SELECT some_id FROM some_Table);
DELETE FROM some_table;
kuda pucat
sumber
12
Ini tidak selalu berhasil karena mungkin ada kunci asing lainnya yang mengalir dari kaskade asli (rekursi). Anda bahkan bisa masuk ke lingkaran di mana tabel a merujuk ke b yang merujuk ke a. Untuk mencapai ini secara umum, lihat tabel saya di bawah ini, tetapi ada beberapa batasan. Jika Anda memiliki pengaturan tabel sederhana kemudian coba kode di atas, lebih mudah untuk memahami apa yang Anda lakukan.
Joe Love
2
Sederhana, aman. Anda harus menjalankannya dalam satu transaksi jika Anda memiliki sisipan kepadatan.
İsmail Yavuz
39

Jika Anda benar-benar menginginkan DELETE FROM some_table CASCADE; yang berarti " hapus semua baris dari tabelsome_table ", Anda dapat menggunakannya TRUNCATEsebagai ganti DELETEdan CASCADEselalu didukung. Namun, jika Anda ingin menggunakan penghapusan selektif dengan whereklausa, TRUNCATEtidak cukup baik.

GUNAKAN DENGAN PERAWATAN - Ini akan menjatuhkan semua baris dari semua tabel yang memiliki batasan kunci asing some_tabledan semua tabel yang memiliki kendala pada tabel tersebut, dll.

Postgres mendukung CASCADEdengan perintah TRUNCATE :

TRUNCATE some_table CASCADE;

Mudahnya ini bersifat transaksional (yaitu dapat diputar kembali), meskipun tidak sepenuhnya terisolasi dari transaksi bersamaan lainnya, dan memiliki beberapa peringatan lainnya. Baca dokumen untuk detailnya.

DanC
sumber
226
jelas "beberapa penghapusan cascading" ≠ menjatuhkan semua data dari tabel ...
lensovet
33
Ini akan menjatuhkan semua baris semua tabel yang memiliki batasan kunci asing pada some_table dan semua tabel yang memiliki batasan pada tabel tersebut, dll ... ini berpotensi sangat berbahaya.
AJP
56
awas. ini jawaban yang gegabah.
Jordan Arseno
4
Seseorang telah menandai jawaban ini untuk dihapus - mungkin karena mereka tidak setuju dengan itu. Tindakan yang benar dalam kasus ini adalah dengan downvote, bukan flag.
Wai Ha Lee
7
Dia memiliki peringatan di atas. Jika Anda memilih untuk mengabaikan itu, tidak ada yang dapat membantu Anda. Saya pikir pengguna "copyPaste" Anda adalah bahaya nyata di sini.
BluE
28

Saya menulis fungsi (rekursif) untuk menghapus baris apa pun berdasarkan kunci utamanya. Saya menulis ini karena saya tidak ingin membuat batasan saya sebagai "pada penghapusan kaskade". Saya ingin dapat menghapus set data yang kompleks (sebagai DBA) tetapi tidak mengizinkan programmer saya untuk dapat menghapus kaskade tanpa memikirkan semua akibatnya. Saya masih menguji fungsi ini, jadi mungkin ada bug di dalamnya - tapi tolong jangan coba-coba jika DB Anda memiliki kunci utama multi-kolom (dan dengan demikian asing). Selain itu, semua kunci harus dapat direpresentasikan dalam bentuk string, tetapi dapat ditulis dengan cara yang tidak memiliki batasan itu. Saya menggunakan fungsi ini SANGAT SPARINGLY, saya terlalu menghargai data saya untuk mengaktifkan kendala cascading pada semuanya. Pada dasarnya fungsi ini diteruskan dalam skema, nama tabel, dan nilai primer (dalam bentuk string), dan itu akan mulai dengan menemukan kunci asing di tabel itu dan memastikan data tidak ada - jika ada, itu secara rekursif menyebut dirinya sendiri pada data yang ditemukan. Ini menggunakan array data yang sudah ditandai untuk dihapus untuk mencegah loop tak terbatas. Silakan mengujinya dan beri tahu saya cara kerjanya untuk Anda. Catatan: Agak lambat. Saya menyebutnya seperti ini: 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;
Joe Love
sumber
Ini terjadi setiap saat terutama dengan tabel referensi diri. Pertimbangkan sebuah perusahaan dengan tingkatan manajemen yang berbeda di departemen yang berbeda, atau taksonomi hierarki generik. Ya, saya setuju bahwa fungsi ini bukan yang terbaik mutlak sejak mengiris roti, tetapi ini adalah alat yang berguna dalam situasi yang tepat.
Joe Love
Jika Anda menulis ulang itu menerima array ID dan juga menghasilkan kueri yang akan menggunakan INoperator dengan sub-pilih bukan =(jadi langkah menggunakan set logika) itu akan menjadi jauh lebih cepat.
Hubbitus
2
Terima kasih atas solusinya. Saya menulis beberapa tes dan saya perlu menghapus catatan dan saya mengalami kesulitan untuk menghapus penghapusan itu. Fungsi Anda bekerja dengan sangat baik!
Fernando Camargo
1
@JoeLove masalah kecepatan apa yang kamu punya? Dalam situasi itu rekursi adalah solusi tunggal yang benar di pikiran saya.
Hubbitus
1
@arthur Anda mungkin bisa menggunakan beberapa versi baris -> json -> teks untuk menyelesaikannya, namun, saya belum melangkah sejauh itu. Saya telah menemukan selama bertahun-tahun bahwa kunci primer tunggal (dengan kunci sekunder potensial) baik untuk banyak alasan.
Joe Love
17

Jika saya mengerti dengan benar, Anda harus dapat melakukan apa yang Anda inginkan dengan menjatuhkan batasan kunci asing, menambahkan yang baru (yang akan mengalir), melakukan hal-hal Anda, dan menciptakan kembali batasan kunci asing yang membatasi.

Sebagai contoh:

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

Tentu saja, Anda harus mengabstraksikan hal-hal seperti itu menjadi sebuah prosedur, demi kesehatan mental Anda.

Ryszard Szopa
sumber
4
Dengan asumsi bahwa skema kunci asing mencegah melakukan hal-hal yang membuat database tidak konsisten, ini bukan cara untuk menghadapinya. Anda dapat menghapus entri "jahat" sekarang tetapi Anda meninggalkan banyak pecahan zombie yang dapat menyebabkan masalah di masa depan
Sprinterfreak
1
Pecahan apa yang Anda maksudkan sebenarnya? catatan akan dihapus melalui kaskade seharusnya tidak ada inkonsistensi.
Pedro Borges
1
daripada mengkhawatirkan tentang "pecahan jahat" (batasan kaskade akan tetap konsisten), saya akan LEBIH lebih peduli tentang kaskade tidak akan cukup jauh - jika catatan yang dihapus memerlukan catatan yang dihapus lebih lanjut, maka batasan tersebut perlu diubah untuk memastikan cascading juga. (atau gunakan fungsi yang saya tulis di atas untuk menghindari skenario ini) ... Satu rekomendasi terakhir dalam kasus apa pun: GUNAKAN TRANSAKSI sehingga Anda dapat mengembalikannya jika sudah salah.
Joe Love
7

Saya tidak bisa mengomentari jawaban Palehorse jadi saya menambahkan jawaban saya sendiri. Logika Palehorse baik-baik saja tetapi efisiensi bisa buruk dengan set data besar.

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;

Lebih cepat jika Anda memiliki indeks pada kolom dan kumpulan data lebih besar dari beberapa catatan.

Grzegorz Grabek
sumber
7

Ya, seperti yang orang lain katakan, tidak ada nyaman 'HAPUS DARI my_table ... CASCADE' (atau setara). Untuk menghapus catatan anak asing yang dilindungi kunci tidak bertingkat dan leluhur yang dirujuk, opsi Anda meliputi:

  • Lakukan semua penghapusan secara eksplisit, satu kueri pada satu waktu, dimulai dengan tabel anak (meskipun ini tidak akan terbang jika Anda punya referensi melingkar); atau
  • Lakukan semua penghapusan secara eksplisit dalam satu kueri (berpotensi besar); atau
  • Dengan asumsi batasan kunci asing non-kaskade Anda dibuat sebagai 'HAPUS HAPUS TANPA TINDAKAN', lakukan semua penghapusan secara eksplisit dalam satu transaksi; atau
  • Jatuhkan sementara 'tidak ada tindakan' dan 'batasi' batasan kunci asing dalam grafik, buat ulang mereka sebagai CASCADE, hapus leluhur yang menyinggung, jatuhkan kendala kunci asing lagi, dan akhirnya buat kembali seperti semula (sehingga melemahkan integritas integritas sementara untuk sementara waktu). data Anda); atau
  • Sesuatu yang mungkin sama-sama menyenangkan.

Ini dengan sengaja bahwa menghindari batasan kunci asing tidak dibuat nyaman, saya berasumsi; tapi saya mengerti mengapa dalam keadaan tertentu Anda ingin melakukannya. Jika itu adalah sesuatu yang akan Anda lakukan dengan frekuensi tertentu, dan jika Anda ingin mengabaikan kebijaksanaan DBA di mana-mana, Anda mungkin ingin mengotomatiskannya dengan prosedur.

Saya datang ke sini beberapa bulan yang lalu mencari jawaban untuk pertanyaan "CASCADE DELETE just once" (awalnya ditanyakan lebih dari satu dekade yang lalu!). Saya mendapatkan jarak tempuh dari solusi cerdas Joe Love (dan varian Thomas CG de Vilhena), tetapi pada akhirnya use case saya memiliki persyaratan tertentu (penanganan referensi melingkar intra-tabel, untuk satu) yang memaksa saya untuk mengambil pendekatan yang berbeda. Pendekatan itu akhirnya menjadi recursively_delete (PG 10.10).

Saya telah menggunakan recursively_delete dalam produksi untuk sementara waktu, sekarang, dan akhirnya merasa (waspada) cukup percaya diri untuk membuatnya tersedia untuk orang lain yang mungkin berakhir di sini mencari ide. Seperti halnya solusi Joe Love, ini memungkinkan Anda untuk menghapus seluruh grafik data seolah-olah semua batasan kunci asing dalam database Anda sesaat disetel ke CASCADE, tetapi menawarkan beberapa fitur tambahan:

  • Menyediakan pratinjau ASCII dari target penghapusan dan grafik tanggungannya.
  • Melakukan penghapusan dalam satu permintaan menggunakan CTE rekursif.
  • Menangani dependensi melingkar, intra-dan antar-tabel.
  • Menangani kunci komposit.
  • Melewati batasan 'set default' dan 'set null'.
TRL
sumber
Saya mendapatkan kesalahan: ERROR: array harus memiliki jumlah elemen genap Di mana: PL / fungsi pgSQL _recursively_delete (regclass, teks [], integer, jsonb, integer, teks [], jsonb, jsonb) baris 15 pada penugasan pernyataan SQL "SELECT * FROM _recursively_delete (ARG_table, VAR_pk_col_names)" Fungsi PL / pgSQL recursively_delete (regclass, anyelement, boolean) baris 73 pada pernyataan SQL
Joe Love
Hai, @JoeLove. Terima kasih telah mencobanya. Bisakah Anda memberi saya langkah untuk mereproduksi? Dan apa versi PG Anda?
TRL
Saya tidak yakin ini akan membantu. tapi saya baru saja membuat fungsi Anda dan kemudian menjalankan kode berikut: pilih recursively_delete ('dallas.vendor', 1094, false) Setelah beberapa debug, saya menemukan bahwa ini mati langsung dari kelelawar - artinya, sepertinya itu adalah panggilan pertama ke fungsi, bukan setelah melakukan banyak hal. Untuk referensi saya menjalankan PG 10.8
Joe Love
@ JoLove, Mohon coba cabang trl-fix-array_must_have_even_number_of_element ( github.com/trlorenz/PG-recursively_delete/pull/2 ).
TRL
Mencoba cabang itu dan itu memperbaiki kesalahan aslinya. Sayangnya, ini tidak lebih cepat dari versi asli saya (yang mungkin bukan maksud Anda menulis ini sejak awal). Saya sedang mengerjakan upaya lain yang membuat duplikat kunci asing dengan "on delete cascade", lalu menghapus catatan asli, lalu menjatuhkan semua kunci asing yang baru dibuat,
Joe Love
3

Anda dapat menggunakan untuk mengotomatisasi ini, Anda dapat menentukan batasan kunci asing ON DELETE CASCADE.
Saya mengutip manual batasan kunci asing :

CASCADE menetapkan bahwa ketika baris yang dirujuk dihapus, baris yang merujinya juga harus dihapus secara otomatis.

atiruz
sumber
1
Meskipun ini tidak membahas OP, perencanaan yang baik untuk saat baris dengan kunci asing perlu dihapus. Seperti yang dikatakan Ben Franklin, "satu ons pencegahan bernilai satu pon penyembuhan."
Jesuisme
1
Saya telah menemukan bahwa solusi ini bisa sangat berbahaya jika aplikasi Anda menghapus catatan dengan banyak saudara kandung dan bukannya kesalahan kecil, Anda telah secara permanen menghapus kumpulan data yang besar.
Joe Love
2

Saya mengambil jawaban Joe Love dan menulis ulang menggunakan INoperator dengan sub-pilihan alih-alih =membuat fungsi lebih cepat (menurut saran 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;
Thomas CG de Vilhena
sumber
2
Saya harus melihat ini dan melihat seberapa baik kerjanya dengan kendala referensi diri dan sejenisnya. Saya mencoba melakukan sesuatu yang serupa tetapi berhenti membuatnya bekerja sepenuhnya. Jika solusi Anda bekerja untuk saya, saya akan mengimplementasikannya. Ini adalah salah satu dari banyak alat dba yang harus dikemas dan disiapkan di github atau apalah.
Joe Love
Saya memiliki database ukuran sedang untuk CMS multi-tenant (semua klien berbagi tabel yang sama). Versi saya (tanpa "dalam") tampaknya berjalan cukup lambat untuk menghapus semua jejak klien lama ... Saya tertarik mencoba ini dengan beberapa data mockup untuk membandingkan kecepatan. Apakah Anda memiliki sesuatu yang dapat Anda katakan tentang perbedaan kecepatan yang Anda perhatikan dalam use case Anda?
Joe Love
Untuk use case saya, saya perhatikan peningkatan dalam urutan 10x saat menggunakan inoperator dan sub-kueri.
Thomas CG de Vilhena
1

Hapus dengan opsi kaskade hanya diterapkan pada tabel dengan kunci asing yang ditentukan. Jika Anda melakukan penghapusan, dan itu mengatakan Anda tidak bisa karena itu akan melanggar batasan kunci asing, kaskade akan menyebabkannya menghapus baris yang menyinggung.

Jika Anda ingin menghapus baris terkait dengan cara ini, Anda harus menentukan kunci asing terlebih dahulu. Juga, ingat bahwa kecuali Anda secara eksplisit memerintahkannya untuk memulai transaksi, atau Anda mengubah default, itu akan melakukan komit otomatis, yang mungkin sangat memakan waktu untuk dibersihkan.

Grant Johnson
sumber
2
Jawaban Grant sebagian salah - Postgresql tidak mendukung CASCADE pada pertanyaan DELETE. postgresql.org/docs/8.4/static/dml-delete.html
Fredrik Wendt
Adakah yang tahu mengapa itu tidak didukung pada permintaan penghapusan?
Teifion
2
tidak ada cara untuk "menghapus dengan kaskade" pada tabel yang belum diatur sesuai, yaitu batasan kunci asing belum didefinisikan sebagai ON DELETE CASCADE, yang merupakan pertanyaan awal.
lensovet
Sebagai jawaban ini pertanyaan, ini benar-benar salah. Tidak ada cara untuk CASCADE sekali.
Jeremy