it-swarm.asia

MySQL القيود الخارجية ، وحذف تتالي

أريد استخدام مفاتيح أجنبية للحفاظ على النزاهة وتجنب الأيتام (أستخدم بالفعل innoDB).

كيف يمكنني إجراء SQL statment التي حذفها على CASCADE؟

إذا قمت بحذف فئة ، فكيف أتأكد من أنها لن تحذف المنتجات المرتبطة أيضًا بفئات أخرى.

ينشئ الجدول المحوري "category_products" علاقة أطراف بأطراف بين الجدولين الآخرين.

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

products
- id
- name
- price

categories_products
- categories_id
- products_id
143
Cudos

إذا حذفت سلسلة متتالية من الأسلحة النووية منتجًا لأنه كان عضواً في فئة تم قتلها ، فعندئذ قمت بإعداد مفاتيحك الخارجية بطريقة غير صحيحة. بالنظر إلى جداول الأمثلة الخاصة بك ، يجب أن يكون لديك إعداد الجدول التالي:

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;

وبهذه الطريقة ، يمكنك حذف منتج OR فئة ، وسوف تموت فقط السجلات المرتبطة في الفئات_المنتجات. لن تنتقل السلسلة إلى أعلى الشجرة وتحذف جدول المنتج/الفئة الأصل.

مثلا.

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". إذا قمت بحذف الفئة "الحمراء" ، فإن السجلات الوحيدة التي ستتتابع الحذف في الفئات_المنتجات هي تلك التي تحتوي على category_id = red. لن يمس أي سجلات حيث 'category_id = blue' ، ولن ينتقل إلى جدول "المنتجات" ، لأنه لا يوجد مفتاح خارجي محدد في هذا الجدول.

إليك مثال أكثر واقعية:

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

سوف ينظر نظام إدارة قواعد البيانات إلى جميع الجداول التي تحتوي على مفتاح خارجي يشير إلى جدول "الفئات" ، ويحذف السجلات التي يكون فيها معرف المطابقة هو 2. بما أننا حددنا علاقة المفتاح الخارجي فقط في products_categories ، فستنتهي بهذا الجدول مرة واحدة اكتمال الحذف:

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

لا يوجد مفتاح خارجي محدد في الجدول products ، لذلك لن يعمل المتتالية هناك ، لذلك لا يزال لديك قائمة بالقفازات والقفازات. لم يعد هناك "أحذية زرقاء" ولا "قفازات زرقاء" بعد الآن.

360
Marc B

لقد شعرت بالارتباك من إجابة هذا السؤال ، لذلك قمت بإنشاء حالة اختبار في 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
11
Abderrahim

أعتقد (لست متأكدًا) أن قيود المفاتيح الخارجية لن تفعل بالضبط ما تريد بالنظر إلى تصميم طاولتك. ربما يكون أفضل ما يمكنك فعله هو تحديد إجراء مخزن يحذف فئة بالطريقة التي تريدها ، ثم استدعاء هذا الإجراء كلما أردت حذف فئة.

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) (حيث class_ID هي الفئة المراد حذفها) ، وسوف تتصرف بالطريقة التي تريدها. ولكن لا تصدر استعلام DELETE FROM عاديًا ، إلا إذا كنت تريد المزيد من السلوك القياسي (أي حذف من جدول الارتباط فقط ، وترك جدول products وحده).

8
Hammerite