एक बहुरूपी संघ में आपकी विदेशी कुंजी क्यों नहीं हो सकती है?


81

आप एक बहुरूपिए संघ में एक विदेशी कुंजी क्यों नहीं रख सकते हैं, जैसे कि नीचे एक रैल मॉडल के रूप में प्रतिनिधित्व किया गया है?

class Comment < ActiveRecord::Base
  belongs_to :commentable, :polymorphic => true
end

class Article < ActiveRecord::Base
  has_many :comments, :as => :commentable
end

class Photo < ActiveRecord::Base
  has_many :comments, :as => :commentable
  #...
end

class Event < ActiveRecord::Base
  has_many :comments, :as => :commentable
end

3
बस दूसरों की स्पष्टता के लिए, ओपी उस foreign_keyविकल्प के बारे में बात नहीं कर रहा है जिसे पारित किया जा सकता है belongs_to। ओपी मूल डेटाबेस के एक "विदेशी कुंजी बाधा" के बारे में बात कर रहा है। इसने मुझे थोड़ी देर के लिए उलझन में डाल दिया।
जोशुआ पिंटर

जवाबों:


178

एक विदेशी कुंजी में केवल एक मूल तालिका का संदर्भ होना चाहिए। यह SQL सिंटैक्स और रिलेशनल सिद्धांत दोनों के लिए मौलिक है।

एक पॉलीमॉर्फिक एसोसिएशन तब होता है जब किसी दिए गए कॉलम में दो या दो से अधिक मूल तालिकाओं का संदर्भ हो सकता है। कोई रास्ता नहीं है कि आप SQL में उस बाधा की घोषणा कर सकते हैं।

पॉलिमॉर्फिक एसोसिएशन डिज़ाइन रिलेशनल डेटाबेस डिज़ाइन के नियमों को तोड़ता है। मैं इसका उपयोग करने की सलाह नहीं देता।

कई विकल्प हैं:

  • विशिष्ट आर्क: कई विदेशी कुंजी कॉलम बनाएँ, प्रत्येक एक माता-पिता को संदर्भित करता है। लागू करें कि इनमें से कोई एक विदेशी कुंजी गैर-पूर्ण हो सकती है।

  • रिलेशनशिप को उल्टा करें : तीन कई-से-कई तालिकाओं का उपयोग करें, प्रत्येक संदर्भ टिप्पणियाँ और एक संबंधित माता-पिता।

  • कंक्रीट सुपरटेबल: निहित "टिप्पणी योग्य" सुपरक्लास के बजाय, एक वास्तविक तालिका बनाएं जो आपके प्रत्येक माता-पिता तालिकाओं का संदर्भ दें। फिर अपनी टिप्पणियों को उस सुपरटेबल से लिंक करें। छद्म रेल कोड कुछ इस तरह होगा (मैं एक रेल उपयोगकर्ता नहीं हूं, इसलिए इसे एक दिशानिर्देश के रूप में मानें, न कि शाब्दिक कोड के रूप में):

    class Commentable < ActiveRecord::Base
      has_many :comments
    end
    
    class Comment < ActiveRecord::Base
      belongs_to :commentable
    end
    
    class Article < ActiveRecord::Base
      belongs_to :commentable
    end
    
    class Photo < ActiveRecord::Base
      belongs_to :commentable
    end
    
    class Event < ActiveRecord::Base
      belongs_to :commentable
    end
    

मैं अपनी प्रस्तुति में पॉलीमॉर्फिक एसोसिएशनों को एसक्यूएल में प्रैक्टिकल ऑब्जेक्ट-ओरिएंटेड मॉडल , और मेरी पुस्तक एसक्यूएल एंटीपैर्टन्स: डेटाबेस प्रोग्रामिंग के नुकसान से बचता हूं


अपनी टिप्पणी को फिर से लिखें: हाँ, मुझे पता है कि एक और स्तंभ है जो उस तालिका का नाम नोट करता है जिसे विदेशी कुंजी माना जाता है। यह डिज़ाइन SQL में विदेशी कुंजियों द्वारा समर्थित नहीं है।

उदाहरण के लिए, यदि आप एक टिप्पणी और नाम "वीडियो" सम्मिलित करते हैं, तो उसके लिए मूल तालिका का नाम Commentक्या है ? "वीडियो" नाम की कोई तालिका मौजूद नहीं है। क्या सम्मिलित त्रुटि के साथ गर्भपात किया जाना चाहिए? किस बाधा का उल्लंघन किया जा रहा है? RDBMS को कैसे पता चलता है कि इस स्तंभ को किसी मौजूदा तालिका का नाम देना है? यह केस-असंवेदनशील तालिका नामों को कैसे संभालता है?

इसी तरह, यदि आप Eventsतालिका को छोड़ देते हैं, लेकिन आपके पास पंक्तियाँ हैं Commentsजो ईवेंट को उनके माता-पिता के रूप में इंगित करती हैं, तो परिणाम क्या होना चाहिए? क्या ड्रॉप टेबल को निरस्त किया जाना चाहिए? पंक्तियों को Commentsअनाथ होना चाहिए ? क्या उन्हें किसी अन्य मौजूदा तालिका जैसे कि को बदलना चाहिए Articles? क्या उस आईडी वैल्यू को इंगित करते हैं Eventsजो किसी ओर इशारा करते समय कोई मतलब निकालने के लिए इस्तेमाल करती है Articles?

ये दुविधाएं इस तथ्य के कारण हैं कि पॉलिमॉर्फिक एसोसिएशन मेटाडेटा (एक तालिका नाम) को संदर्भित करने के लिए डेटा (यानी एक स्ट्रिंग मूल्य) का उपयोग करने पर निर्भर करता है। यह SQL द्वारा समर्थित नहीं है। डेटा और मेटाडेटा अलग-अलग हैं।


मुझे आपके "कंक्रीट सुपरटेबल" प्रस्ताव के चारों ओर अपना सिर लपेटने में मुश्किल समय हो रहा है।

  • Commentableएक वास्तविक एसक्यूएल तालिका के रूप में परिभाषित करें , न केवल आपके रेल मॉडल परिभाषा में एक विशेषण। कोई अन्य कॉलम आवश्यक नहीं है।

    CREATE TABLE Commentable (
      id INT AUTO_INCREMENT PRIMARY KEY
    ) TYPE=InnoDB;
    
  • तालिकाओं को परिभाषित करें Articles, Photosऔर Events"उपवर्गों" के रूप में की Commentableहै, उनके प्राथमिक कुंजी भी एक विदेशी कुंजी संदर्भित होना बनाकर Commentable

    CREATE TABLE Articles (
      id INT PRIMARY KEY, -- not auto-increment
      FOREIGN KEY (id) REFERENCES Commentable(id)
    ) TYPE=InnoDB;
    
    -- similar for Photos and Events.
    
  • Commentsएक विदेशी कुंजी के साथ तालिका को परिभाषित करें Commentable

    CREATE TABLE Comments (
      id INT PRIMARY KEY AUTO_INCREMENT,
      commentable_id INT NOT NULL,
      FOREIGN KEY (commentable_id) REFERENCES Commentable(id)
    ) TYPE=InnoDB;
    
  • जब आप Article(उदाहरण के लिए) बनाना चाहते हैं , तो आपको एक नई पंक्ति Commentableभी बनानी होगी । तो भी के लिए Photosऔर Events

    INSERT INTO Commentable (id) VALUES (DEFAULT); -- generate a new id 1
    INSERT INTO Articles (id, ...) VALUES ( LAST_INSERT_ID(), ... );
    
    INSERT INTO Commentable (id) VALUES (DEFAULT); -- generate a new id 2
    INSERT INTO Photos (id, ...) VALUES ( LAST_INSERT_ID(), ... );
    
    INSERT INTO Commentable (id) VALUES (DEFAULT); -- generate a new id 3
    INSERT INTO Events (id, ...) VALUES ( LAST_INSERT_ID(), ... );
    
  • जब आप एक बनाना चाहते हैं Comment, तो उस मान का उपयोग करें जो मौजूद है Commentable

    INSERT INTO Comments (id, commentable_id, ...)
    VALUES (DEFAULT, 2, ...);
    
  • जब आप किसी दिए गए टिप्पणियों की क्वेरी करना चाहते हैं Photo, तो कुछ सम्मिलित करें:

    SELECT * FROM Photos p JOIN Commentable t ON (p.id = t.id)
    LEFT OUTER JOIN Comments c ON (t.id = c.commentable_id)
    WHERE p.id = 2;
    
  • जब आपके पास केवल एक टिप्पणी की आईडी होती है और आप यह जानना चाहते हैं कि यह किस उल्लेखनीय संसाधन के लिए टिप्पणी है। इसके लिए, आप पा सकते हैं कि यह उल्लेखनीय तालिका के लिए उपयोगी है कि यह किस संसाधन को संदर्भित करता है।

    SELECT commentable_id, commentable_type FROM Commentable t
    JOIN Comments c ON (t.id = c.commentable_id)
    WHERE c.id = 42;
    

    फिर आपको संबंधित संसाधन तालिका (फ़ोटो, लेख, आदि) से डेटा प्राप्त करने के लिए दूसरी क्वेरी को चलाने की आवश्यकता है, जिसके बाद पता चलता है commentable_typeकि किस तालिका से जुड़ना है। आप इसे उसी क्वेरी में नहीं कर सकते, क्योंकि एसक्यूएल की आवश्यकता है कि तालिकाओं को स्पष्ट रूप से नामित किया जाए; आप उसी क्वेरी में डेटा परिणामों द्वारा निर्धारित तालिका में शामिल नहीं हो सकते।

बेशक, इन चरणों में से कुछ रेल द्वारा उपयोग किए गए सम्मेलनों को तोड़ते हैं। लेकिन उचित रिलेशनल डेटाबेस डिज़ाइन के संबंध में रेल कन्वेंशन गलत हैं।


2
फॉलो करने के लिए धन्यवाद। बस इसलिए हम एक ही पेज पर हैं, रेल पॉलिमॉर्फिक एसोसिएशन में विदेशी कुंजी के लिए हमारी टिप्पणी में दो कॉलम का उपयोग करते हैं। एक स्तंभ लक्ष्य पंक्ति की आईडी रखता है, और दूसरा स्तंभ सक्रिय रिकॉर्ड बताता है कि कौन सा मॉडल कुंजी में है (अनुच्छेद, फोटो या घटना)। यह जानकर, क्या आप अभी भी उन तीन विकल्पों की सिफारिश करेंगे जो आपने प्रस्तावित किए हैं? मुझे आपके "कंक्रीट सुपरटेबल" प्रस्ताव के चारों ओर अपना सिर लपेटने में मुश्किल समय हो रहा है। जब आप कहते हैं कि "आपकी टिप्पणियों को उस सुपरटेबल से लिंक करें" (टिप्पणी योग्य) का क्या अर्थ है?
eggdrop

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

7
बिल्कुल सही। यह एक मजबूत "कोड गंध" होना चाहिए कि यह संबंधपरक डेटाबेस डिज़ाइन को सही नहीं करता है जब पॉलीमॉर्फिक एसोसिएशन के दस्तावेज़ स्वयं कहते हैं कि आप विदेशी कुंजी बाधाओं का उपयोग नहीं कर सकते हैं!
बिल कार्विन

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

1
@ मोहम्मद, एसटीआई ठीक काम करेगा। यदि आपकी मूल तालिका STI का उपयोग करती है, तो आप अभी भी विदेशी कुंजी परिभाषित कर सकते हैं। या भले ही बच्चे की तालिका में एसटीआई का उपयोग किया गया हो।
बिल कार्विन

3

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

CREATE FUNCTION delete_related_brokerage_subscribers() RETURNS trigger AS $$
  BEGIN
    DELETE FROM subscribers
    WHERE referrer_type = 'Brokerage' AND referrer_id = OLD.id;
    RETURN NULL;
  END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER cascade_brokerage_subscriber_delete
AFTER DELETE ON brokerages
FOR EACH ROW EXECUTE PROCEDURE delete_related_brokerage_subscribers();


CREATE FUNCTION delete_related_agent_subscribers() RETURNS trigger AS $$
  BEGIN
    DELETE FROM subscribers
    WHERE referrer_type = 'Agent' AND referrer_id = OLD.id;
    RETURN NULL;
  END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER cascade_agent_subscriber_delete
AFTER DELETE ON agents
FOR EACH ROW EXECUTE PROCEDURE delete_related_agent_subscribers();

मेरे कोड में brokeragesतालिका में एक रिकॉर्ड या तालिका में एक रिकॉर्ड agentsतालिका में रिकॉर्ड से संबंधित हो सकता है subscribers


यह भी खूब रही। यह सुनिश्चित करने के लिए कि कोई व्यक्ति समान ट्रिगर कैसे बना सकता है, यह सुनिश्चित करने के लिए कि नव-निर्मित बहुरूपी संघ एक वैध प्रकार और आईडी की ओर संकेत करते हैं?
cabblood
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.