MySQL 외래 키 제약 조건, 계단식 삭제


158

무결성을 유지하고 고아를 피하기 위해 외래 키를 사용하고 싶습니다 (이미 innoDB를 사용하고 있습니다).

CASCADE에서 DELETE ON SQL 문을 작성하려면 어떻게해야합니까?

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

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

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

products
- id
- name
- price

categories_products
- categories_id
- products_id

안녕하세요-질문 제목을 수정하고 싶을 수도 있습니다. 캐스케이드 삭제, 특히 피벗 테이블이 아닙니다.
Paddyslacker

답변:


387

계단식으로 인해 제품이 종료 된 카테고리의 구성원이기 때문에 핵무기를 삭제하는 경우 외래 키를 잘못 설정 한 것입니다. 예제 테이블이 주어지면 다음 테이블 설정이 있어야합니다.

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

'빨간색'카테고리를 삭제하면 카테고리 테이블의 '빨간색'항목과 '빨간색 부츠'및 '빨간색 코트'라는 두 항목 prod / cats 만 죽습니다.

삭제는 더 이상 계단식으로 연결되지 않으며 '부츠'및 '코트'범주를 제거하지 않습니다.

댓글 후속 조치 :

계단식 삭제가 작동하는 방식을 여전히 이해하고 있습니다. "삭제시 계단식"이 정의 된 테이블에만 영향을줍니다. 이 경우 캐스케이드는 "categories_products"테이블에서 설정됩니다. '빨간색'카테고리를 삭제하면 categories_products에서 연속 삭제되는 레코드는 where 레코드 만 있습니다 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는 '범주'테이블을 가리키는 외래 키가있는 모든 테이블을보고 일치하는 ID가 2 인 레코드를 삭제합니다.에서 외래 키 관계 만 정의 했으므로이 products_categories테이블은 삭제 완료 :

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

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


내 질문을 잘못 쓴 것 같습니다. 카테고리를 삭제하면 다른 카테고리와 관련된 제품도 삭제되지 않도록하려면 어떻게해야합니까?
Cudos

36
이것은 정말 훌륭하고 눈에 잘 띄며 훌륭하게 설명 된 답변입니다. 시간을내어 작성해 주셔서 감사합니다.
scottb

2
테이블을 생성 할 때 InnoDB 또는 CASCADE작동 가능한 다른 MySQL 엔진을 지정해야 합니다. 그렇지 않으면 MySQL 기본값 인 MyISAM이 사용되고 MyISAM은 CASCADE작업을 지원하지 않습니다 . 이렇게하려면 ENGINE InnoDB마지막 앞에 추가 하십시오 ;.
Patrick

11

이 질문에 대한 답변으로 혼란스러워서 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

8

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

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

물론 CONSTRAINT 절은 CREATE TABLE 문에도 나타날 수 있습니다.

이러한 스키마 개체를 만든 후에는 범주를 삭제하고 발급 CALL DeleteCategory(category_ID)(카테고리 _ID는 삭제할 범주)을 실행하여 원하는 동작을 얻을 수 있으며 원하는 방식으로 작동합니다. 그러나 DELETE FROM더 표준적인 동작을 원치 않는 한 (예 : 연결 테이블에서만 삭제하고 products테이블은 그대로 두십시오) 일반적인 쿼리를 실행하지 마십시오 .


내 질문을 잘못 쓴 것 같습니다. 카테고리를 삭제하면 다른 카테고리와 관련된 제품도 삭제되지 않도록하려면 어떻게해야합니까?
Cudos

이 경우에는 Marc B의 답변이 원하는대로 작동한다고 생각합니다.
Hammerite

안녕하세요 @Hammerite, KEY pkey (product_id),세 번째 CREATE TABLE쿼리에서 허용 된 답변 의 의미가 무엇인지 말씀해 주 시겠습니까?
Siraj Alam
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.