2010-05-26 1 views
124

무결성을 유지하고 고아를 피하기 위해 외래 키를 사용하고 싶습니다 (이미 innoDB를 사용하고 있습니다).MySQL 외래 키 제약 조건, 캐스케이드 삭제

캐스케이드에서 삭제하는 SQL 구문은 어떻게 작성합니까?

카테고리를 삭제하면 다른 카테고리와 관련된 제품도 삭제되지 않도록하려면 어떻게해야합니까?

피벗 테이블 "categories_products"는 다른 두 테이블간에 다 대다 관계를 만듭니다.

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

products 
- id 
- name 
- price 

categories_products 
- categories_id 
- products_id 
+0

안녕하세요 도움이되기를 바랍니다 - 당신이 질문의 제목을 변경 할 수 있습니다, 캐스케이드, 정말로 삭제에 대해 그것의 특별히 피벗 테이블. – Paddyslacker

답변

333

계단식으로 삭제 된 항목이 사망 한 범주의 구성원이기 때문에 제품을 삭제 한 경우 부적절하게 외래 키를 설정했습니다.

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; 

이 방법, 당신은 제품 또는 카테고리, 만 categories_products에 관련 기록이 함께 죽을 것이다 삭제할 ​​수 있습니다 : 귀하의 예제 테이블을 감안할 때, 당신은 다음 표 설정이 있어야합니다. 캐스케이드는 트리 위로 멀리 이동하지 않고 상위 제품/카테고리 테이블을 삭제합니다.

예컨대

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

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

당신이 '빨간색'카테고리, 카테고리 테이블에서 다음 만이 '빨간색'항목 사망뿐만 아니라 두 항목 자극/고양이를 삭제하면

: '빨간 부츠'와 '빨간 코트'.

삭제가 더 이상 수행되지 않으며 '부츠'및 '외투'카테고리가 삭제되지 않습니다.

코멘트 후속 : 당신은 여전히 ​​계단식 어떻게 오해하고

작업을 삭제합니다. "on delete cascade"가 정의 된 테이블에만 영향을 미칩니다. 이 경우 계단식은 "categories_products"테이블에서 설정됩니다. 'red'카테고리를 삭제하면 categories_products에서 연속 삭제 될 레코드는 category_id = red입니다. 'category_id = blue'의 레코드를 건드리지 않으며, 해당 테이블에 외래 키가 정의되어 있지 않기 때문에 "products"테이블로 넘어 가지 않습니다.여기

좀 더 구체적인 예입니다 :

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

의 당신이 카테고리 # 2 (파란색)을 삭제한다고 가정 해 봅시다 :

DELETE FROM categories WHERE (id = 2); 

DBMS에가 외래 키 포인팅에있는 모든 테이블을 볼 것이다 '카테고리'표는, 그리고 우리는 products_categories에 외래 키 관계를 정의하기 때문에 일치하는 ID가 2 인 레코드를 삭제, 당신은이 테이블에 결국 삭제 완료되면 :

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

products 테이블에는 외래 키가 정의되어 있지 않으므로 캐스케이드가 작동하지 않으므로 여전히 부츠와 장갑이 나열되어 있습니다. 더 이상 '블루 부츠'가없고 '블루 벙어리 장갑'도 없습니다.

+0

나는 내 질문을 잘못된 방법으로 썼다고 생각한다. 카테고리를 삭제하면 다른 카테고리와 관련된 제품도 삭제되지 않도록 어떻게해야합니까? – Cudos

+25

이것은 정말 훌륭하고, 매우 눈에 잘 띄며, 훌륭하게 설명 된 대답입니다. 시간을내어 주셔서 감사합니다. – scottb

+1

테이블을 생성 할 때 InnoDB 또는 'CASCADE'연산이 가능한 다른 MySQL 엔진을 지정해야합니다. 그렇지 않으면 MySQL 디폴트 인 MyISAM이 사용되며 MyISAM은'CASCADE' 연산을 지원하지 않습니다. 이렇게하려면, 마지막';'앞에'ENGINE InnoDB'를 추가하십시오. – Patrick

7

외래 키 제약 조건은 사용자가 원하는 테이블 디자인을 정확하게 수행하지 못할 것이라고 생각합니다. 아마도 가장 좋은 방법은 원하는 방식으로 범주를 삭제할 저장 프로 시저를 정의한 다음 범주를 삭제할 때마다 해당 프로 시저를 호출하는 것입니다.

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 

는 또한 연결 테이블에 다음과 같은 외래 키 제약 조건을 추가해야

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 

제약 절은 물론, 또한 CREATE TABLE 문에 나타날 수 있습니다.

이러한 스키마 개체를 생성하면 CALL DeleteCategory(category_ID) (category_ID는 삭제할 범주 임)을 발행하여 범주를 삭제하고 원하는 동작을 얻을 수 있으며 원하는대로 동작합니다. 더 일반적인 동작 (연결 표에서만 삭제)을 원하고 products 표만 남겨 두지 않는 한 일반적인 DELETE FROM 검색어를 발행하지 마세요.

+0

나는 내 질문을 잘못된 길로 썼다고 생각한다. 카테고리를 삭제하면 다른 카테고리와 관련된 제품도 삭제되지 않도록 어떻게해야합니까? – Cudos

+0

그래, 그런 경우에는 Marc B의 대답이 당신이 원하는대로한다고 생각합니다. – Hammerite

9

나는이 질문에 대한 대답 혼란, 그래서 MySQL의에서 테스트 케이스를 생성되었다가,이

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