pengembangan-web-mp.com

Kendala kunci asing MySQL, penghapusan kaskade

Saya ingin menggunakan kunci asing untuk menjaga integritas dan menghindari anak yatim (saya sudah menggunakan innoDB).

Bagaimana cara membuat statemen SQL yang HAPUS DI CASCADE?

Jika saya menghapus kategori maka bagaimana saya memastikan bahwa itu tidak akan menghapus produk yang juga terkait dengan kategori lain.

Tabel pivot "categories_products" menciptakan hubungan banyak-ke-banyak antara dua tabel lainnya.

categories
- id (INT)
- name (VARCHAR 255)

products
- id
- name
- price

categories_products
- categories_id
- products_id
150
Cudos

Jika cascading Anda menghapus nuke produk karena itu adalah anggota dari kategori yang terbunuh, maka Anda telah mengatur kunci asing Anda dengan tidak tepat. Diberikan tabel contoh Anda, Anda harus memiliki pengaturan tabel berikut:

CREATE TABLE categories (
    id int unsigned not null primary key,
    name VARCHAR(255) default null
)Engine=InnoDB;

CREATE TABLE products (
    id int unsigned not null primary key,
    name VARCHAR(255) default null
)Engine=InnoDB;

CREATE TABLE categories_products (
    category_id int unsigned not null,
    product_id int unsigned not null,
    PRIMARY KEY (category_id, product_id),
    KEY pkey (product_id),
    FOREIGN KEY (category_id) REFERENCES categories (id)
       ON DELETE CASCADE
       ON UPDATE CASCADE,
    FOREIGN KEY (product_id) REFERENCES products (id)
       ON DELETE CASCADE
       ON UPDATE CASCADE
)Engine=InnoDB;

Dengan cara ini, Anda dapat menghapus suatu produk OR suatu kategori, dan hanya catatan yang terkait dalam kategori_produk yang akan mati bersamanya. Kaskade tidak akan berjalan lebih jauh ke pohon dan menghapus tabel kategori produk/induk.

misalnya.

products: boots, mittens, hats, coats
categories: red, green, blue, white, black

prod/cats: red boots, green mittens, red coats, black hats

Jika Anda menghapus kategori 'merah', maka hanya entri 'merah' di tabel kategori yang mati, serta dua entri prod/kucing: 'sepatu merah' dan 'mantel merah'.

Penghapusan tidak akan menyebabkan penurunan lebih jauh dan tidak akan menghapus kategori 'sepatu bot' dan 'mantel'.

tindak lanjut komentar:

anda masih salah paham bagaimana cascade delases bekerja. Mereka hanya mempengaruhi tabel di mana "pada penghapusan kaskade" didefinisikan. Dalam kasus ini, kaskade diatur dalam tabel "categories_products". Jika Anda menghapus kategori 'merah', satu-satunya catatan yang akan mengalir menghapus dalam kategori_produk adalah yang mana category_id = red. Itu tidak akan menyentuh catatan mana pun di mana 'category_id = biru', dan itu tidak akan melanjutkan perjalanan ke tabel "produk", karena tidak ada kunci asing yang ditentukan dalam tabel itu.

Inilah contoh yang lebih konkret:

categories:     products:
+----+------+   +----+---------+
| id | name |   | id | name    |
+----+------+   +----+---------+
| 1  | red  |   | 1  | mittens |
| 2  | blue |   | 2  | boots   |
+---++------+   +----+---------+

products_categories:
+------------+-------------+
| product_id | category_id |
+------------+-------------+
| 1          | 1           | // red mittens
| 1          | 2           | // blue mittens
| 2          | 1           | // red boots
| 2          | 2           | // blue boots
+------------+-------------+

Katakanlah Anda menghapus kategori # 2 (biru):

DELETE FROM categories WHERE (id = 2);

dBMS akan melihat semua tabel yang memiliki kunci asing menunjuk pada tabel 'kategori', dan menghapus catatan di mana id yang cocok adalah 2. Karena kami hanya mendefinisikan hubungan kunci asing di products_categories, Anda berakhir dengan tabel ini setelah penghapusan selesai:

+------------+-------------+
| product_id | category_id |
+------------+-------------+
| 1          | 1           | // red mittens
| 2          | 1           | // red boots
+------------+-------------+

Tidak ada kunci asing yang ditentukan dalam tabel products, sehingga kaskade tidak akan berfungsi di sana, jadi Anda masih memiliki bot dan sarung tangan yang terdaftar. Tidak ada lagi 'sepatu bot biru' dan 'sarung tangan biru' lagi.

368
Marc B

Saya bingung dengan jawaban untuk pertanyaan ini, jadi saya membuat test case di MySQL, semoga ini membantu

-- Schema
CREATE TABLE T1 (
    `ID` int not null auto_increment,
    `Label` varchar(50),
    primary key (`ID`)
);

CREATE TABLE T2 (
    `ID` int not null auto_increment,
    `Label` varchar(50),
    primary key (`ID`)
);

CREATE TABLE TT (
    `IDT1` int not null,
    `IDT2` int not null,
    primary key (`IDT1`,`IDT2`)
);

ALTER TABLE `TT`
    ADD CONSTRAINT `fk_tt_t1` FOREIGN KEY (`IDT1`) REFERENCES `T1`(`ID`) ON DELETE CASCADE,
    ADD CONSTRAINT `fk_tt_t2` FOREIGN KEY (`IDT2`) REFERENCES `T2`(`ID`) ON DELETE CASCADE;

-- Data
INSERT INTO `T1` (`Label`) VALUES ('T1V1'),('T1V2'),('T1V3'),('T1V4');
INSERT INTO `T2` (`Label`) VALUES ('T2V1'),('T2V2'),('T2V3'),('T2V4');
INSERT INTO `TT` (`IDT1`,`IDT2`) VALUES
(1,1),(1,2),(1,3),(1,4),
(2,1),(2,2),(2,3),(2,4),
(3,1),(3,2),(3,3),(3,4),
(4,1),(4,2),(4,3),(4,4);

-- Delete
DELETE FROM `T2` WHERE `ID`=4; -- Delete one field, all the associated fields on tt, will be deleted, no change in T1
TRUNCATE `T2`; -- Can't truncate a table with a referenced field
DELETE FROM `T2`; -- This will do the job, delete all fields from T2, and all associations from TT, no change in T1
11
Abderrahim

Saya pikir (saya tidak yakin) bahwa batasan kunci asing tidak akan melakukan apa yang Anda inginkan dengan desain meja Anda. Mungkin hal terbaik yang harus dilakukan adalah mendefinisikan prosedur tersimpan yang akan menghapus kategori seperti yang Anda inginkan, dan kemudian memanggil prosedur itu setiap kali Anda ingin menghapus kategori.

CREATE PROCEDURE `DeleteCategory` (IN category_ID INT)
LANGUAGE SQL
NOT DETERMINISTIC
MODIFIES SQL DATA
SQL SECURITY DEFINER
BEGIN

DELETE FROM
    `products`
WHERE
    `id` IN (
        SELECT `products_id`
        FROM `categories_products`
        WHERE `categories_id` = category_ID
    )
;

DELETE FROM `categories`
WHERE `id` = category_ID;

END

Anda juga perlu menambahkan batasan kunci asing berikut ke tabel tautan:

ALTER TABLE `categories_products` ADD
    CONSTRAINT `Constr_categoriesproducts_categories_fk`
    FOREIGN KEY `categories_fk` (`categories_id`) REFERENCES `categories` (`id`)
    ON DELETE CASCADE ON UPDATE CASCADE,
    CONSTRAINT `Constr_categoriesproducts_products_fk`
    FOREIGN KEY `products_fk` (`products_id`) REFERENCES `products` (`id`)
    ON DELETE CASCADE ON UPDATE CASCADE

Klausa CONSTRAINT dapat, tentu saja, juga muncul dalam pernyataan CREATE TABLE.

Setelah membuat objek skema ini, Anda dapat menghapus kategori dan mendapatkan perilaku yang Anda inginkan dengan mengeluarkan CALL DeleteCategory(category_ID) (di mana category_ID adalah kategori yang akan dihapus), dan itu akan berperilaku seperti yang Anda inginkan. Tapi jangan mengeluarkan kueri DELETE FROM yang normal, kecuali jika Anda menginginkan lebih banyak perilaku standar (mis. Hapus hanya dari tabel yang menautkan, dan biarkan tabel products sendirian).

8
Hammerite