Ograniczenia klucza obcego MySQL, kaskadowe usuwanie

Chcę używać kluczy obcych, aby zachować integralność i uniknąć sierot (już używam innoDB).

Jak zrobić statment SQL, że usunąć na kaskadzie?

Jeśli usunę kategorię, to jak mam się upewnić, że nie usunie produktów, które również są powiązane z innymi kategoriami.

Tabela przestawna "categories_products" tworzy relację wiele do wielu między dwoma innymi tabelami.

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

products
- id
- name
- price

categories_products
- categories_id
- products_id
Author: Cudos, 2010-05-26

3 answers

Jeśli kaskadowo usuniesz nuke produkt, ponieważ należał do kategorii, która została zabita, to nieprawidłowo skonfigurowałeś swoje klucze obce. Biorąc pod uwagę przykładowe tabele, powinieneś mieć następującą konfigurację tabeli:

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;

W ten sposób możesz usunąć produkt lub kategorię, a tylko powiązane rekordy w categories_products zginą obok. Kaskada nie przesunie się dalej w górę drzewa i nie usunie tabeli nadrzędnego produktu/kategorii.

Np.

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

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

Jeśli Usuń kategorię "red", wtedy w tabeli kategorii umiera tylko wpis "red", a także dwa wpisy prod / cats: "red boots" i "red coats".

Usuń nie będzie dalej kaskadowo i nie usunie kategorii "Buty" i "płaszcze".

Komentarz:

Nadal nie rozumiesz, jak działają kaskadowe usuwanie. Wpływają one tylko na tabele, w których zdefiniowana jest kaskada "on delete". W tym przypadku kaskada jest ustawiona w tabeli "categories_products". Jeśli usuniesz kategorię "red", jedynymi rekordami, które zostaną kaskadowo usunięte w categories_products są te, w których category_id = red. Nie dotknie żadnych rekordów, w których 'category_id = blue', i nie przejdzie dalej do tabeli "products", ponieważ w tej tabeli nie ma zdefiniowanego klucza obcego.

Oto bardziej konkretny przykład:

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

Powiedzmy, że usuniesz kategorię # 2 (Niebieska):

DELETE FROM categories WHERE (id = 2);

DBMS przejrzy wszystkie tabele z kluczem obcym wskazującym na 'kategorie' tabelę i Usuń rekordy, w których pasującym id jest 2. Ponieważ zdefiniowaliśmy relację klucza obcego tylko w products_categories, Kończy się ona w tej tabeli po zakończeniu usuwania:

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

Nie ma zdefiniowanego klucza obcego w tabeli products, więc kaskada nie będzie tam działać, więc nadal masz wymienione Buty i rękawiczki. Nie ma już "niebieskich butów" ani "niebieskich rękawiczek".

 349
Author: Marc B,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-09-20 01:01:13

Zdezorientowała mnie odpowiedź na to pytanie, więc stworzyłem przypadek testowy w MySQL, mam nadzieję, że to pomoże

-- 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
Author: Abderrahim,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-04-24 09:53:38

Myślę (nie jestem pewien), że ograniczenia klucza obcego nie zrobią dokładnie tego, co chcesz, biorąc pod uwagę projekt stołu. Być może najlepszą rzeczą do zrobienia jest zdefiniowanie procedury składowanej, która usunie kategorię tak, jak chcesz, a następnie wywoła tę procedurę, gdy chcesz usunąć 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

Należy również dodać następujące ograniczenia klucza obcego do tabeli łączenia:

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

Klauzula ograniczenia może oczywiście pojawić się również w tabeli CREATE oświadczenie.

Po utworzeniu tych obiektów schematu, możesz usunąć kategorię i uzyskać pożądane zachowanie, wydając CALL DeleteCategory(category_ID) (gdzie category_ID jest kategorią do usunięcia), a będzie ona zachowywać się tak, jak chcesz. Ale nie wysyłaj normalnego zapytania DELETE FROM, chyba że chcesz bardziej standardowego zachowania(tzn. usuń tylko tabelę łączącą i zostaw samą tabelę products).

 8
Author: Hammerite,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-05-26 20:48:38