2010-05-26 11 views
124

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

मैं एक SQL स्टेटमेंट कैसे बना सकता हूं जो कैस्केड पर हटा देता है?

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

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

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 

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

डिलीट किसी भी आगे कैस्केड नहीं करेगा और 'जूते' और 'कोट' श्रेणियां नहीं लेगा।

टिप्पणी फॉलोअप:

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

+0

मुझे लगता है कि मैंने अपना प्रश्न गलत तरीके से लिखा था। अगर मैं एक वर्ग और फिर हटाएं कैसे मुझे यकीन है कि यह उत्पाद है कि अन्य श्रेणियों से संबंधित हैं को नष्ट नहीं होगा बनाते हैं। – Cudos

+25

यह एक सच में महान अत्यधिक सुबोध, और शानदार सचित्र जवाब है। इसे लिखने के लिए समय निकालने के लिए धन्यवाद। – scottb

+1

टेबल बनाते समय, आपको InnoDB या अन्य MySQL इंजन निर्दिष्ट करना होगा जो 'CASCADE' संचालन में सक्षम है। अन्यथा MySQL डिफ़ॉल्ट, MyISAM का उपयोग किया जाएगा और MyISAM 'CASCADE 'संचालन का समर्थन नहीं करता है। ऐसा करने के लिए, अंतिम ';' से पहले 'इंजन 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 श्रेणी हटाया जाने वाला है) से आप चाहते हैं, और यह आप कैसे चाहते व्यवहार करेगा। लेकिन जब तक आप अधिक मानक व्यवहार नहीं चाहते हैं, तो सामान्य DELETE FROM क्वेरी जारी न करें (यानी केवल लिंकिंग तालिका से हटाएं, और products अकेले तालिका छोड़ दें)।

+0

मुझे लगता है कि मैं अपने प्रश्न गलत तरीके से लिखा था। अगर मैं एक वर्ग और फिर हटाएं कैसे मुझे यकीन है कि यह उत्पाद है कि अन्य श्रेणियों से संबंधित हैं को नष्ट नहीं होगा बनाते हैं। – Cudos

+0

ठीक अच्छी तरह से उस मामले में मुझे लगता है कि मार्क बी का जवाब आप क्या चाहते हैं नहीं करता है। – 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 
संबंधित मुद्दे