विदेशी मुख्य बाधा चक्र या कई कैस्केड पथ का कारण हो सकता है?


176

जब मैं अपनी मेज पर अड़चन जोड़ने की कोशिश करता हूं तो मुझे एक समस्या होती है। मुझे त्रुटि मिली:

'फ़ॉइल' पर 'फ़ॉइल 74888DB24B3C886' फ़ॉरेस्ट की मुख्य बाधा का परिचय, चक्र या कई कैस्केड पथ का कारण हो सकता है। DELETE NO ACTION या UPDATE NO ACTION को निर्दिष्ट करें, या अन्य FOREIGN कुंजी बाधाओं को संशोधित करें।

मेरी अड़चन एक Codeटेबल और टेबल के बीच में है employeeCodeतालिका में शामिल है Id, Name, FriendlyName, Typeऔर एक Valueemployeeफ़ील्ड संदर्भ कोड, इसलिए वहाँ कोड के प्रत्येक प्रकार के लिए एक संदर्भ हो सकता है कि की एक संख्या है।

यदि फ़ील्ड को संदर्भित किया जाता है, तो मैं रिक्त करने के लिए फ़ील्ड को सेट करने की आवश्यकता है।

किसी भी विचार मैं यह कैसे कर सकते हैं?


समाधान में से एक यहाँ है
इस्माइल

जवाबों:


180

SQL सर्वर कैस्केड रास्तों की सरल गणना करता है और यह जानने की कोशिश करता है कि क्या वास्तव में कोई भी चक्र मौजूद है, यह सबसे खराब मानता है और संदर्भात्मक क्रियाओं (CASCADE) को बनाने से इंकार करता है: आप अभी भी रेफरेंशियल कार्यों के बिना बाधाओं को बना सकते हैं और करना चाहिए। यदि आप अपने डिज़ाइन को बदल नहीं सकते हैं (या ऐसा करने से चीजों से समझौता होगा) तो आपको ट्रिगर्स को अंतिम उपाय के रूप में उपयोग करने पर विचार करना चाहिए।

एफडब्ल्यूआईडब्ल्यू कैस्केड पथों को हल करना एक जटिल समस्या है। अन्य एसक्यूएल उत्पाद बस समस्या को नजरअंदाज करेंगे और आपको साइकिल बनाने की अनुमति देंगे, इस स्थिति में यह देखने के लिए एक दौड़ होगी जो मूल्य को अंतिम रूप से लिख देगा, शायद डिजाइनर की अनदेखी (जैसे एसीई / जेट ऐसा करता है)। मैं समझता हूं कि कुछ SQL उत्पाद सरल मामलों को हल करने का प्रयास करेंगे। तथ्य यह है कि, SQL सर्वर भी कोशिश नहीं करता है, इसे एक से अधिक पथ को अस्वीकार करके अल्ट्रा सुरक्षित निभाता है और कम से कम यह आपको ऐसा बताता है।

Microsoft स्वयं FK बाधाओं के बजाय ट्रिगर्स के उपयोग की सलाह देता है।


2
एक बात जो मुझे अभी भी समझ नहीं आ रही है, वह यह है कि यदि इस "समस्या" को एक ट्रिगर का उपयोग करके हल किया जा सकता है, तो यह कैसे आता है कि एक ट्रिगर "चक्र या कई कैस्केड पथ का कारण नहीं होगा ..."?
आर्म

5
@armen: क्योंकि आपका ट्रिगर स्पष्ट रूप से इस तर्क की आपूर्ति करेगा कि सिस्टम अपने आप ही यह पता नहीं लगा सकता है कि यदि कोई डिलीट रिफ़रेंशियल एक्शन के लिए कई रास्ते हैं तो आपका ट्रिगर कोड परिभाषित करेगा कि कौन सी टेबल डिलीट की गई हैं और किस क्रम में हैं।
onedaywhen

6
और पहले ऑपरेशन के पूरा होने के बाद भी ट्रिगर निष्पादित होता है ताकि कोई दौड़ न हो।
बॉन

2
@ डंबल्डड: मेरा मतलब है, केवल तभी ट्रिगर का उपयोग करें जब बाधाएं (शायद संयोजन पर) काम नहीं कर सकती हैं। अड़चनें घोषणात्मक हैं और उनका कार्यान्वयन प्रणाली की जिम्मेदारी है। ट्रिगर प्रक्रियात्मक कोड हैं और आपको कार्यान्वयन (और डिबग) को कोड करना चाहिए और उनके नुकसान (खराब प्रदर्शन आदि) को सहना होगा।
onedaywhen

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

99

कई कैस्केडिंग रास्तों के साथ एक विशिष्ट स्थिति यह होगी: दो विवरणों के साथ एक मास्टर टेबल, चलो "मास्टर" और "डिटेल 1" और "डिटेल 2" कहें। दोनों विवरण कैस्केड डिलीट हैं। अब तक कोई समस्या नहीं। लेकिन क्या होगा यदि दोनों विवरणों का किसी अन्य तालिका के साथ एक-से-कई संबंध हो ("SomeOtherTable")। SomeOtherTable में एक Detail1ID-कॉलम और एक Detail2ID-कॉलम है।

Master { ID, masterfields }

Detail1 { ID, MasterID, detail1fields }

Detail2 { ID, MasterID, detail2fields }

SomeOtherTable {ID, Detail1ID, Detail2ID, someothertablefields }

दूसरे शब्दों में: SomeOtherTable में कुछ रिकॉर्ड्स को Detail1- रिकॉर्ड के साथ जोड़ा गया है और SomeOtherTable में कुछ रिकॉर्ड्स को Detail2 रिकॉर्ड के साथ जोड़ा गया है। यहां तक ​​कि अगर यह गारंटी दी जाती है कि SomeOtherTable-Records दोनों विवरणों से संबंधित नहीं हैं, तो अब SomeOhterTable के रिकॉर्ड को दोनों विवरणों के लिए कैस्केड करना असंभव है, क्योंकि मास्टर से SomeOtherTable (एकाधिक के माध्यम से एक और विस्तार 2 के माध्यम से एक) के लिए कई कैस्केडिंग पथ हैं। अब आप यह पहले ही समझ गए होंगे। यहाँ एक संभावित समाधान है:

Master { ID, masterfields }

DetailMain { ID, MasterID }

Detail1 { DetailMainID, detail1fields }

Detail2 { DetailMainID, detail2fields }

SomeOtherTable {ID, DetailMainID, someothertablefields }

सभी आईडी फ़ील्ड की-फ़ील्ड और ऑटो-इन्क्रीमेंट हैं। क्रूक्स विस्तार तालिकाओं के DetailMainId क्षेत्रों में निहित है। ये क्षेत्र कुंजी और संदर्भात्मक दोनों हैं। अब केवल मास्टर-रिकॉर्ड्स को हटाकर सबकुछ मिटा देना संभव है। नकारात्मक पक्ष यह है कि प्रत्येक डिटेल 1-रिकॉर्ड के लिए और प्रत्येक डिटेल 2 रिकॉर्ड के लिए, एक डिटेलमैन-रिकॉर्ड भी होना चाहिए (जो वास्तव में सही और अनोखी आईडी प्राप्त करने के लिए पहले बनाया गया है)।


1
आपकी टिप्पणी से मुझे उस समस्या को समझने में बहुत मदद मिली जो मैं झेल रहा हूं। धन्यवाद! मैं रास्ते में से एक के लिए कैस्केड हटाने को बंद करना पसंद करूंगा, फिर अन्य रिकॉर्डों को हटाने के कुछ अन्य तरीके (संग्रहीत; ट्रिगर; कोड आदि द्वारा) को संभालना होगा। लेकिन मैं एक ही समस्या के संभावित विभिन्न अनुप्रयोगों के लिए मन में अपने समाधान (एक रास्ते में समूहीकरण) रखना ...
स्वेच्छाबलि

1
क्रुक्स शब्द का उपयोग करने के लिए एक (और समझाने के लिए भी)
मास्टरवॉक

क्या यह ट्रिगर लिखने से बेहतर है? कैस्केड को काम करने के लिए एक अतिरिक्त तालिका जोड़ना अजीब लगता है।
dumbledad

ट्रिगर लिखने से कुछ भी बेहतर है। उनका तर्क अपारदर्शी है और वे किसी भी चीज़ की तुलना में अक्षम हैं। महीन नियंत्रण के लिए बड़ी मेजों को छोटे-छोटे हिस्सों में तोड़ना एक बेहतर सामान्यीकृत डेटाबेस का स्वाभाविक परिणाम है और इससे संबंधित होने के लिए अपने आप में कुछ नहीं है।
न्यूट्रिनो

12

मैं बताता हूं कि (कार्यात्मक रूप से) SCHEMA और DATA में साइकिल और / या कई रास्तों के बीच एक बड़ा अंतर है। हालांकि DATA में चक्र और शायद मल्टीपाथ निश्चित रूप से प्रसंस्करण को जटिल कर सकते हैं और प्रदर्शन समस्याओं ("ठीक से" हैंडलिंग की लागत) का कारण बन सकते हैं, स्कीमा में इन विशेषताओं की लागत शून्य के करीब होनी चाहिए।

चूंकि RDB में अधिकांश स्पष्ट चक्र पदानुक्रमित संरचनाओं (ऑर्ग चार्ट, भाग, सबपार्टर, आदि) में होते हैं, यह दुर्भाग्यपूर्ण है कि SQL सर्वर सबसे खराब मानता है; यानी, स्कीमा चक्र == डेटा चक्र। वास्तव में, यदि आप RI बाधाओं का उपयोग कर रहे हैं, तो आप वास्तव में डेटा में एक चक्र का निर्माण नहीं कर सकते हैं!

मुझे संदेह है कि मल्टीपाथ समस्या समान है; यानी, स्कीमा में कई पथ आवश्यक रूप से डेटा में कई पथ नहीं लगाते हैं, लेकिन मुझे बहुपथ समस्या के साथ कम अनुभव है।

बेशक अगर SQL सर्वर ने चक्रों की अनुमति दी थी तो यह अभी भी 32 की गहराई के अधीन होगा, लेकिन यह ज्यादातर मामलों के लिए पर्याप्त है। (हालांकि यह एक डेटाबेस सेटिंग नहीं है बहुत बुरा है!)

"हटाएँ के बजाय" ट्रिगर या तो काम नहीं करते हैं। दूसरी बार एक तालिका का दौरा किया जाता है, ट्रिगर को अनदेखा किया जाता है। इसलिए, यदि आप वास्तव में एक झरना का अनुकरण करना चाहते हैं, तो आपको चक्र की उपस्थिति में संग्रहीत प्रक्रियाओं का उपयोग करना होगा। इसके बजाय डिलीट-ट्रिगर मल्टीपैथ मामलों के लिए काम करेगा।

सेलको, पदानुक्रम का प्रतिनिधित्व करने के लिए एक "बेहतर" तरीका सुझाता है जो साइकिल का परिचय नहीं देता है, लेकिन ट्रेडऑफ़ हैं।


"यदि आप RI बाधाओं का उपयोग कर रहे हैं तो आप वास्तव में डेटा में एक चक्र का निर्माण नहीं कर सकते हैं!" -- अच्छी बात!
onedaywhen

सुनिश्चित करें कि आप डेटा परिपत्र का निर्माण कर सकते हैं, लेकिन MSSQL के साथ केवल UPDATE का उपयोग कर सकते हैं। अन्य RDBMs आस्थगित अवरोधों का समर्थन करते हैं (सम्मिलित / अद्यतन / हटाए जाने के समय नहीं)
कार्ल क्रिग

7

इसमें एक लेख उपलब्ध है जिसमें बताया गया है कि ट्रिगर्स का उपयोग करते हुए कई डिलीट पथ को कैसे करें। शायद यह जटिल परिदृश्यों के लिए उपयोगी है।

http://www.mssqltips.com/sqlservertip/2733/solving-the-sql-server-multiple-cascade-path-issue-with-a-trigger/


3

इसकी ध्वनियों से आपके पास अपने मौजूदा फॉरेन कीज़ में से किसी एक पर OnDelete / OnUpdate एक्शन होता है, जो आपके कोड टेबल को संशोधित करेगा।

तो इस विदेशी कुंजी को बनाकर, आप एक चक्रीय समस्या पैदा करेंगे;

उदाहरण के लिए, कर्मचारियों को अपडेट करना, अपडेट एक्शन द्वारा कोड बदलने का कारण बनता है, कर्मचारियों को ऑन अपडेट एक्शन ... आदि से बदल दिया जाता है।

यदि आप अपनी तालिका परिभाषाएँ दोनों तालिकाओं के लिए पोस्ट करते हैं, और आपकी विदेशी कुंजी / बाधा परिभाषाएँ हमें आपको यह बताने में सक्षम होना चाहिए कि समस्या कहाँ है ...


1
वे काफी लंबे हैं, इसलिए मुझे नहीं लगता कि मैं उन्हें यहां पोस्ट कर सकता हूं, लेकिन मैं आपकी मदद की बहुत सराहना करूंगा - न ही पता है कि क्या कोई रास्ता है जो मैं उन्हें आपके पास भेज सकता हूं? बीमार कोशिश करते हैं और इसका वर्णन करते हैं: केवल मौजूद बाधाएं 3 तालिकाओं से होती हैं जो सभी में एक साधारण INT आईडी कुंजी द्वारा संदर्भ कोड वाले फ़ील्ड होते हैं। समस्या यह प्रतीत होती है कि कर्मचारी के पास कई फ़ील्ड हैं जो कोड तालिका को संदर्भित करते हैं और यह कि मैं उन सभी को SET NULL को कैस्केड करना चाहता हूं। मुझे केवल इतना ही चाहिए कि जब कोड हटा दिए जाएं, तो उनके संदर्भ को सभी जगह शून्य कर दिया जाए।

वैसे भी उन्हें पोस्ट करें ... मुझे नहीं लगता कि यहां कोई भी मन करेगा, और कोड विंडो उन्हें स्क्रॉलिंग ब्लॉक में ठीक से प्रारूपित करेगी :)
इयोन कैंपबेल

2

ऐसा इसलिए है क्योंकि Emplyee का कहना है कि अन्य संस्था का संग्रह हो सकता है योग्यता और योग्यता के पास कुछ अन्य संग्रह विश्वविद्यालय जैसे हो सकते हैं

public class Employee{
public virtual ICollection<Qualification> Qualifications {get;set;}

}

public class Qualification{

public Employee Employee {get;set;}

public virtual ICollection<University> Universities {get;set;}

}

public class University{

public Qualification Qualification {get;set;}

}

DataContext पर यह नीचे की तरह हो सकता है

protected override void OnModelCreating(DbModelBuilder modelBuilder){

modelBuilder.Entity<Qualification>().HasRequired(x=> x.Employee).WithMany(e => e.Qualifications);
modelBuilder.Entity<University>.HasRequired(x => x.Qualification).WithMany(e => e.Universities);

}

इस मामले में कर्मचारी से योग्यता तक और विश्वविद्यालयों से योग्यता तक की श्रृंखला है। तो यह मेरे लिए एक ही अपवाद फेंक रहा था।

जब मैंने बदला तो यह मेरे लिए काम कर गया

    modelBuilder.Entity<Qualification>().**HasRequired**(x=> x.Employee).WithMany(e => e.Qualifications); 

सेवा

    modelBuilder.Entity<Qualification>().**HasOptional**(x=> x.Employee).WithMany(e => e.Qualifications);

1

ट्रिगर इस समस्या का हल है:

IF OBJECT_ID('dbo.fktest2', 'U') IS NOT NULL
    drop table fktest2
IF OBJECT_ID('dbo.fktest1', 'U') IS NOT NULL
    drop table fktest1
IF EXISTS (SELECT name FROM sysobjects WHERE name = 'fkTest1Trigger' AND type = 'TR')
    DROP TRIGGER dbo.fkTest1Trigger
go
create table fktest1 (id int primary key, anQId int identity)
go  
    create table fktest2 (id1 int, id2 int, anQId int identity,
        FOREIGN KEY (id1) REFERENCES fktest1 (id)
            ON DELETE CASCADE
            ON UPDATE CASCADE/*,    
        FOREIGN KEY (id2) REFERENCES fktest1 (id) this causes compile error so we have to use triggers
            ON DELETE CASCADE
            ON UPDATE CASCADE*/ 
            )
go

CREATE TRIGGER fkTest1Trigger
ON fkTest1
AFTER INSERT, UPDATE, DELETE
AS
    if @@ROWCOUNT = 0
        return
    set nocount on

    -- This code is replacement for foreign key cascade (auto update of field in destination table when its referenced primary key in source table changes.
    -- Compiler complains only when you use multiple cascased. It throws this compile error:
    -- Rrigger Introducing FOREIGN KEY constraint on table may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, 
    -- or modify other FOREIGN KEY constraints.
    IF ((UPDATE (id) and exists(select 1 from fktest1 A join deleted B on B.anqid = A.anqid where B.id <> A.id)))
    begin       
        update fktest2 set id2 = i.id
            from deleted d
            join fktest2 on d.id = fktest2.id2
            join inserted i on i.anqid = d.anqid        
    end         
    if exists (select 1 from deleted)       
        DELETE one FROM fktest2 one LEFT JOIN fktest1 two ON two.id = one.id2 where two.id is null -- drop all from dest table which are not in source table
GO

insert into fktest1 (id) values (1)
insert into fktest1 (id) values (2)
insert into fktest1 (id) values (3)

insert into fktest2 (id1, id2) values (1,1)
insert into fktest2 (id1, id2) values (2,2)
insert into fktest2 (id1, id2) values (1,3)

select * from fktest1
select * from fktest2

update fktest1 set id=11 where id=1
update fktest1 set id=22 where id=2
update fktest1 set id=33 where id=3
delete from fktest1 where id > 22

select * from fktest1
select * from fktest2

0

यह प्रकार डेटाबेस ट्रिगर नीतियों की एक त्रुटि है। ट्रिगर एक कोड है और कैस्केड संबंध जैसे कैस्केड संबंध में कुछ समझदारी या शर्तों को जोड़ सकता है। आपको इसके आसपास संबंधित तालिकाओं के विकल्पों की आवश्यकता हो सकती है जैसे कि कासकेडऑनलाइन बंद करना :

protected override void OnModelCreating( DbModelBuilder modelBuilder )
{
    modelBuilder.Entity<TableName>().HasMany(i => i.Member).WithRequired().WillCascadeOnDelete(false);
}

या इस सुविधा को पूरी तरह से बंद कर दें:

modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();

-2

ASP.NET Core 2.0 और EF Core 2.0 का उपयोग करके इस समस्या का मेरा समाधान निम्नलिखित क्रम में करना था:

  1. update-databaseडेटाबेस बनाने के लिए पैकेज मैनेजमेंट कंसोल (PMC) में कमांड चलाएं (यह "फॉरेन डिजाइन की बाधा का परिचय देता है ... चक्र या एकाधिक कैस्केड पथ का कारण हो सकता है।" त्रुटि)

  2. script-migration -Idempotentएक स्क्रिप्ट बनाने के लिए पीएमसी में कमांड चलाएं जो मौजूदा तालिकाओं / बाधाओं की परवाह किए बिना चलाया जा सकता है

  3. परिणामी स्क्रिप्ट लें और ढूंढें ON DELETE CASCADEऔर उसके साथ बदलेंON DELETE NO ACTION

  4. डेटाबेस के खिलाफ संशोधित SQL निष्पादित करें

अब, आपका माइग्रेशन अप-टू-डेट होना चाहिए और कैस्केडिंग डिलीट नहीं होना चाहिए।

बहुत बुरा मुझे एंटिटी फ्रेमवर्क कोर 2.0 में ऐसा करने का कोई तरीका नहीं मिल रहा था।

सौभाग्य!


ऐसा करने के लिए आप अपनी माइग्रेशन फ़ाइल को बदल सकते हैं (sql स्क्रिप्ट को बदले बिना), यानी अपनी माइग्रेशन फ़ाइल में आप कैस्केड से प्रतिबंधित करने के लिए onDelete क्रिया सेट कर सकते हैं
Rushi

यह धाराप्रवाह एनोटेशन का उपयोग करके इसे निर्दिष्ट करना बेहतर है ताकि आपको अपने माइग्रेशन फ़ोल्डर को हटाने और पुनः बनाने के लिए ऐसा करने के लिए याद न करना पड़े।
एलन वैंग

मेरे अनुभव में, धाराप्रवाह एनोटेशन का उपयोग किया जा सकता है और इसका उपयोग किया जाना चाहिए (मैं उनका उपयोग करता हूं) लेकिन वे अक्सर काफी छोटी-छोटी होती हैं। बस उन्हें कोड में निर्दिष्ट करना हमेशा अपेक्षित परिणाम नहीं देता है।
user1477388
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.