MySQL विदेशी प्रमुख बाधाओं, कैस्केड हटाएं


158

मैं अखंडता बनाए रखने और अनाथों से बचने के लिए विदेशी कुंजियों का उपयोग करना चाहता हूं (मैं पहले से ही innoDB का उपयोग करता हूं)।

मैं कैसे एक SQL क़ानून बनाऊं जो CASCADE पर DELETE हो?

अगर मैं कोई श्रेणी हटाता हूं तो मैं यह कैसे सुनिश्चित करूं कि यह उन उत्पादों को नहीं हटाएगा जो अन्य श्रेणियों से संबंधित हैं।

पिवट टेबल "कैटेगरी_प्रोडक्ट्स" दो अन्य तालिकाओं के बीच कई-से-कई संबंध बनाती है।

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

products
- id
- name
- price

categories_products
- categories_id
- products_id

हाय - आप प्रश्न शीर्षक को संशोधित करना चाह सकते हैं, यह कैस्केड के बारे में वास्तव में हटाता है, विशेष रूप से पिवट टेबल नहीं।
पैड्सलिकर

जवाबों:


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;

इस तरह, आप एक उत्पाद या एक श्रेणी को हटा सकते हैं, और केवल श्रेणियों में संबंधित रिकॉर्ड_प्रोडक्ट्स के साथ मर जाएंगे। कैसकेड पेड़ पर आगे नहीं जाएगा और मूल उत्पाद / श्रेणी तालिका को हटा देगा।

जैसे

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

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

यदि आप 'लाल' श्रेणी को हटाते हैं, तो श्रेणियों की तालिका में केवल 'लाल' प्रविष्टि मर जाती है, साथ ही दो प्रविष्टियाँ ठेस / बिल्लियाँ: 'लाल बूट' और 'लाल कोट'।

डिलीट करने से कोई फ़र्क नहीं पड़ेगा और 'बूट्स' और 'कोट्स' कैटेगिरी नहीं निकालेगा।

टिप्पणी अनुवर्ती:

आप अभी भी गलत समझ रहे हैं कि कैस्केड कैसे काम करता है। वे केवल उन तालिकाओं को प्रभावित करते हैं जिनमें "डिलीट कैस्केड" को परिभाषित किया गया है। इस मामले में, कैस्केड को "श्रेणियां_प्रोडक्ट" तालिका में सेट किया गया है। यदि आप 'रेड' श्रेणी को हटाते हैं, तो कैकेड_प्रोडक्ट्स में कैस्केड को हटाने वाले एकमात्र रिकॉर्ड वे हैं जहां category_id = red। यह किसी भी रिकॉर्ड को नहीं छूएगा, जहां 'श्रेणी_आईडी = नीला' है, और यह "उत्पादों" तालिका पर आगे की यात्रा नहीं करेगा, क्योंकि उस तालिका में कोई विदेशी कुंजी निर्धारित नहीं है।

यहाँ एक और अधिक ठोस उदाहरण दिया गया है:

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 उन सभी तालिकाओं को देखेगा, जिनमें 'श्रेणियों' की तालिका की ओर इशारा करते हुए एक विदेशी कुंजी है, और उन अभिलेखों को हटा दें जहां मिलान आईडी 2 है। चूंकि हम केवल विदेशी कुंजी संबंध को परिभाषित करते हैं products_categories, आप एक बार इस तालिका के साथ समाप्त होते हैं। पूर्णताएं हटाएं:

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

productsतालिका में कोई विदेशी कुंजी निर्धारित नहीं है , इसलिए कैस्केड वहां काम नहीं करेगा, इसलिए आपको अभी भी बूट और मिट्टन्स सूचीबद्ध हैं। अब कोई 'ब्लू बूट' नहीं है और न ही 'ब्लू म्यूटेंस' है।


मुझे लगता है कि मैंने अपने प्रश्न को गलत तरीके से लिखा है। यदि मैं एक श्रेणी हटाता हूं तो मैं यह कैसे सुनिश्चित करूं कि यह उन उत्पादों को नहीं हटाएगा जो अन्य श्रेणियों से संबंधित हैं।
कुडोस

36
यह वास्तव में बहुत ही बढ़िया, अत्यधिक विशिष्ट और आश्चर्यजनक रूप से सचित्र उत्तर है। यह सब लिखने के लिए समय निकालने के लिए धन्यवाद।
स्कॉटलैंड

2
तालिकाओं को बनाते समय, आपको InnoDB या किसी अन्य MySQL इंजन को निर्दिष्ट करने की आवश्यकता होती है जो CASCADEसंचालन में सक्षम हो । अन्यथा MySQL डिफ़ॉल्ट, MyISAM, का उपयोग किया जाएगा और MyISAM CASCADEसंचालन का समर्थन नहीं करता है। ऐसा करने के लिए, बस ENGINE InnoDBअंतिम से पहले जोड़ें ;
पैट्रिक

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)(जहाँ category_ID को हटाने की श्रेणी है), और यह व्यवहार करेगा कि आप कैसे चाहते हैं। लेकिन सामान्य DELETE FROMक्वेरी जारी न करें , जब तक कि आप अधिक मानक व्यवहार नहीं चाहते (यानी लिंकिंग टेबल से हटा दें, और productsअकेले तालिका छोड़ दें )।


मुझे लगता है कि मैंने अपने प्रश्न को गलत तरीके से लिखा है। यदि मैं एक श्रेणी हटाता हूं तो मैं यह कैसे सुनिश्चित करूं कि यह उन उत्पादों को नहीं हटाएगा जो अन्य श्रेणियों से संबंधित हैं।
कुडोस

ठीक है उस मामले में मुझे लगता है कि मार्क बी का जवाब वही है जो आप चाहते हैं।
हैमराइट

नमस्कार @ Hammerite, क्या आप मुझे बता सकते हैं कि स्वीकृत उत्तर में KEY pkey (product_id),तीसरी CREATE TABLEक्वेरी का क्या अर्थ है ?
सिराज आलम
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.