2010-05-26 14 views
124

Ich möchte Fremdschlüssel verwenden, um die Integrität zu erhalten und Waisen zu vermeiden (ich verwende bereits innoDB).MySQL Fremdschlüssel Einschränkungen, Kaskade löschen

Wie mache ich eine SQL-Anweisung, die auf CASCADE LÖSCHEN?

Wenn ich eine Kategorie lösche, wie stelle ich sicher, dass sie keine Produkte löscht, die auch mit anderen Kategorien verwandt sind.

Die Pivot-Tabelle "categories_products" erstellt eine Viele-zu-Viele-Beziehung zwischen den beiden anderen Tabellen.

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

products 
- id 
- name 
- price 

categories_products 
- categories_id 
- products_id 
+0

Hallo hilft - man könnte die Frage Titel ändern möchten, es geht um Kaskade wirklich löscht, nicht speziell Pivot-Tabellen. – Paddyslacker

Antwort

333

Wenn Ihre Kaskadierung ein Produkt löscht, weil es ein Mitglied einer Kategorie war, die getötet wurde, haben Sie Ihre Fremdschlüssel nicht ordnungsgemäß eingerichtet. Angesichts Ihrer Beispiel Tabellen, sollten Sie die folgende Tabelle Setup:

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; 

Auf diese Weise können Sie ein Produkt oder eine Kategorie löschen, und nur die zugehörigen Datensätze in categories_products wird neben sterben. Die Kaskade wird nicht weiter den Baum hinauf und löscht die Elternprodukt/Kategorietabelle.

z.B.

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

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

Wenn Sie die ‚rote‘ Kategorie löschen, dann nur den ‚roten‘ Eintrag in der Tabelle Kategorien stirbt, sowie die beiden Einträge prod/Katzen: ‚red Stiefel‘ und ‚rote Mäntel‘.

Das Löschen wird nicht weiter kaskadieren und die Kategorien "Boots" und "Mäntel" werden nicht entfernt.

Kommentar Followup:

Sie noch Missverständnis, wie kaskadierte löscht Arbeit. Sie betreffen nur die Tabellen, in denen die "on delete cascade" definiert ist. In diesem Fall wird die Kaskade in der Tabelle "categories_products" festgelegt. Wenn Sie die Kategorie "red" löschen, werden nur die Datensätze in categories_products gelöscht, bei denen category_id = red kaskadiert wird. Es wird keine Datensätze berühren, bei denen 'category_id = blau' ist, und es würde nicht weiter zur Tabelle 'products' gehen, da in dieser Tabelle kein Fremdschlüssel definiert ist.

Hier ist ein konkreteres Beispiel:

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 
+------------+-------------+ 

die Sie Kategorie # 2 (blau) Lassen Sie sagen, löschen:

DELETE FROM categories WHERE (id = 2); 

das DBMS in allen Tabellen, die auf eine Fremdschlüssel Zeige haben bei Löschen Sie die Datensätze mit der übereinstimmenden ID 2. Da wir nur die Fremdschlüsselbeziehung in products_categories definiert haben, erhalten Sie diese Tabelle nach Abschluss des Löschvorgangs:

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

In der Tabelle products ist kein Fremdschlüssel definiert, daher funktioniert die Kaskade dort nicht, Sie haben also immer noch Stiefel und Handschuhe aufgelistet. Es gibt einfach keine blauen Stiefel und keine blauen Handschuhe mehr.

+0

Ich glaube, ich habe meine Frage falsch geschrieben. Wenn ich eine Kategorie lösche, wie stelle ich sicher, dass keine Produkte gelöscht werden, die auch mit anderen Kategorien in Verbindung stehen. – Cudos

+25

Dies ist eine wirklich großartige, sehr gut verständliche und wunderbar bebilderte Antwort. Danke, dass du dir die Zeit genommen hast, alles aufzuschreiben. – scottb

+1

Beim Erstellen der Tabellen müssen Sie InnoDB oder eine andere MySQL-Engine angeben, die CASCADE-Operationen ausführen kann. Andernfalls wird der MySQL-Standard MyISAM verwendet, und MyISAM unterstützt keine CASCADE-Operationen. Fügen Sie dazu einfach 'ENGINE InnoDB' vor dem letzten'; 'hinzu. – Patrick

7

denke ich (ich bin mir nicht sicher), dass Fremdschlüssel-Constraints nicht genau das tun, was Sie Ihren Tisch Design gegeben werden sollen. Am besten ist es, eine gespeicherte Prozedur zu definieren, die eine Kategorie wie gewünscht löscht und diese Prozedur dann aufruft, wenn Sie eine Kategorie löschen möchten.

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 

Sie müssen außerdem die folgenden Fremdschlüssel-Constraints auf die Verknüpfungstabelle hinzuzufügen:

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 

Die CONSTRAINT-Klausel kann natürlich auch in der Anweisung CREATE TABLE angezeigt.

Nachdem Sie diese Schemaobjekte erstellt haben, können Sie eine Kategorie löschen und das gewünschte Verhalten erhalten, indem Sie CALL DeleteCategory(category_ID) ausgeben (wobei Kategorie_ID die Kategorie ist, die gelöscht werden soll) und sich wie gewünscht verhält. Geben Sie jedoch keine normale DELETE FROM-Abfrage aus, es sei denn, Sie möchten mehr Standardverhalten (d. H. Nur aus der Verknüpfungstabelle löschen und die Tabelle products allein lassen).

+0

Ich glaube, ich habe meine Frage falsch geschrieben. Wenn ich eine Kategorie lösche, wie stelle ich sicher, dass keine Produkte gelöscht werden, die auch mit anderen Kategorien in Verbindung stehen. – Cudos

+0

ok gut in diesem Fall denke ich Marc B's Antwort tut was du willst. – Hammerite

9

Ich habe die Antwort auf diese Frage verwirrt, so habe ich ein Testfall in MySQL, hoffen, dass diese

-- 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 
Verwandte Themen