MySQL yabancı anahtar kısıtlamaları, kademeli silme


158

Bütünlüğü korumak ve yetimleri önlemek için yabancı anahtarlar kullanmak istiyorum (zaten innoDB kullanıyorum).

CASCADE ÜZERİNDEN SİLEN SQL ifadesini nasıl yapabilirim?

Bir kategoriyi silersem, diğer kategorilerle de alakalı ürünleri silmemesini nasıl sağlarım.

Pivot tablo "category_products" diğer iki tablo arasında çoktan çoğa ilişki oluşturur.

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

products
- id
- name
- price

categories_products
- categories_id
- products_id

Merhaba - soru başlığını değiştirmek isteyebilirsiniz, bu özellikle pivot tabloları değil, gerçekten basamaklı silmelerle ilgilidir.
Paddyslacker

Yanıtlar:


387

Basamaklılığınız, öldürülen bir kategorinin üyesi olduğu için nuke bir ürünü silerse, yabancı anahtarlarınızı yanlış ayarladınız. Örnek tablolarınız göz önüne alındığında, aşağıdaki tablo ayarlarına sahip olmalısınız:

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;

Bu şekilde, bir ürünü VEYA bir kategoriyi silebilirsiniz ve sadece category_products içindeki ilişkili kayıtlar birlikte ölür. Art arda sıralı ağaca doğru ilerlemez ve ana ürün / kategori tablosunu silmez.

Örneğin

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

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

'Kırmızı' kategorisini silerseniz, kategoriler tablosundaki yalnızca 'kırmızı' girişin yanı sıra prod / cat adlı iki giriş de ölür: 'kırmızı çizmeler' ve 'kırmızı ceketler'.

Silme işlemi daha fazla basamaklandırılmaz ve 'bot' ve 'kat' kategorilerini çıkarmaz.

yorum takibi:

Basamaklı silme işlemlerinin nasıl çalıştığını hala yanlış anlıyorsunuz. Yalnızca "silme kaskatında" tanımlanmış tabloları etkiler. Bu durumda, kaskad "category_products" tablosunda ayarlanır. 'Kırmızı' kategoriyi silerseniz, category_products öğesinde silme aşamalı olarak silinecek kayıtlar yalnızca bu kategorilerdir category_id = red. 'Category_id = blue' ifadesinin bulunduğu herhangi bir kayda dokunmaz ve "ürünler" tablosuna ilerlemez, çünkü o tabloda tanımlanmış yabancı anahtar yoktur.

İşte daha somut bir örnek:

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

Diyelim ki kategori 2'yi (mavi) sildiniz:

DELETE FROM categories WHERE (id = 2);

DBMS, 'kategoriler' tablosuna işaret eden bir yabancı anahtar bulunan tüm tablolara bakacak ve eşleşen kimliğin 2 olduğu kayıtları silecektir. Yalnızca yabancı anahtar ilişkisini tanımladığımız için products_categories, silme tamamlanır:

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

productsTabloda tanımlanmış bir yabancı anahtar yoktur , bu yüzden kaskad orada çalışmaz, bu yüzden listelenen bot ve eldivenleriniz var. Artık sadece 'mavi bot' ve 'mavi eldiven' yok.


Sanırım sorumu yanlış yazdım. Bir kategoriyi silersem, diğer kategorilerle de alakalı ürünleri silmemesini nasıl sağlarım.
Cudos

36
Bu gerçekten harika, son derece perspektifli ve harika resimli bir cevap. Her şeyi yazmak için zaman ayırdığınız için teşekkür ederiz.
scottb

2
Tabloları oluştururken, InnoDB veya CASCADEişlem yapabilen başka bir MySQL motoru belirtmeniz gerekir . Aksi halde MySQL varsayılanı MyISAM kullanılır ve MyISAM CASCADEişlemleri desteklemez . Bunu yapmak için ENGINE InnoDB, sondan önce ekleyin ;.
Patrick

11

Bu sorunun cevabı karıştı, bu yüzden MySQL'de bir test örneği oluşturdum, umarım bu yardımcı olur

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

Yabancı anahtar kısıtlamalarının tablo tasarımınız göz önüne alındığında tam olarak istediğiniz şeyi yapamayacağını düşünüyorum (emin değilim). Belki de yapılacak en iyi şey, bir kategoriyi istediğiniz gibi silen saklı bir yordam tanımlamak ve ardından bir kategoriyi silmek istediğinizde bu yordamı çağırmaktır.

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

Ayrıca, bağlantı tablosuna aşağıdaki yabancı anahtar kısıtlamalarını eklemeniz gerekir:

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 deyimi, elbette, CREATE TABLE ifadesinde de görünebilir.

Bu şema nesnelerini oluşturduktan sonra, bir kategoriyi silebilir ve istediğiniz davranışı elde edebilirsiniz CALL DeleteCategory(category_ID)(burada category_ID silinecek kategoridir) ve istediğiniz şekilde davranır. Ancak DELETE FROM, daha standart davranışlar istemediğiniz sürece normal bir sorgu vermeyin (örn. Yalnızca bağlantı tablosundan silin ve productstabloyu yalnız bırakın ).


Sanırım sorumu yanlış yazdım. Bir kategoriyi silersem, diğer kategorilerle de alakalı ürünleri silmemesini nasıl sağlarım.
Cudos

tamam bu durumda Marc B'nin cevabı istediğini yapar sanırım.
Hammerite

Merhaba @Hammerite, lütfen kabul edilen cevapta KEY pkey (product_id),üçüncü CREATE TABLEsorguda ne anlama geldiğini söyleyebilir misiniz ?
Siraj Alam
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.