आप एक डेटाबेस में विरासत का प्रतिनिधित्व कैसे कर सकते हैं?


236

मैं एक SQL सर्वर डेटाबेस में एक जटिल संरचना का प्रतिनिधित्व करने के तरीके के बारे में सोच रहा हूं।

एक ऐसे आवेदन पर विचार करें जिसमें वस्तुओं के परिवार के विवरणों को संग्रहीत करने की आवश्यकता होती है, जो कुछ विशेषताओं को साझा करते हैं, लेकिन कई अन्य सामान्य नहीं हैं। उदाहरण के लिए, एक वाणिज्यिक बीमा पैकेज में समान पॉलिसी रिकॉर्ड के भीतर देयता, मोटर, संपत्ति और क्षतिपूर्ति कवर शामिल हो सकते हैं।

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

मैं देख सकता हूँ कि दो मुख्य विकल्प हैं:

  1. सभी संभावित विविधताओं के लिए आवश्यक सभी क्षेत्रों के साथ एक नीति तालिका, फिर एक अनुभाग तालिका बनाएं, जिसमें से अधिकांश शून्य होगी।

  2. प्रत्येक प्रकार के कवर के लिए एक पॉलिसी टेबल और कई सेक्शन टेबल बनाएं।

ये दोनों विकल्प असंतोषजनक प्रतीत होते हैं, विशेष रूप से क्योंकि यह सभी अनुभागों में प्रश्न लिखने के लिए आवश्यक है, जिसमें कई जोड़, या कई अशक्त-चेक शामिल होंगे।

इस परिदृश्य के लिए सबसे अच्छा अभ्यास क्या है?


जवाबों:


430

@Bill Karwin ने SQL एंटिटी-एट्रीब्यूट-वैल्यू एंटीपैटर्न के समाधान का प्रस्ताव करते हुए, अपनी SQL Antipatterns पुस्तक में तीन विरासत मॉडल का वर्णन किया है । यह एक संक्षिप्त अवलोकन है:

एकल तालिका वंशानुक्रम (उर्फ तालिका प्रति पदानुक्रम वंशानुक्रम):

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

+------+---------------------+----------+----------------+------------------+
| id   | date_issued         | type     | vehicle_reg_no | property_address |
+------+---------------------+----------+----------------+------------------+
|    1 | 2010-08-20 12:00:00 | MOTOR    | 01-A-04004     | NULL             |
|    2 | 2010-08-20 13:00:00 | MOTOR    | 02-B-01010     | NULL             |
|    3 | 2010-08-20 14:00:00 | PROPERTY | NULL           | Oxford Street    |
|    4 | 2010-08-20 15:00:00 | MOTOR    | 03-C-02020     | NULL             |
+------+---------------------+----------+----------------+------------------+

\------ COMMON FIELDS -------/          \----- SUBTYPE SPECIFIC FIELDS -----/

डिजाइन को सरल रखना एक प्लस है, लेकिन इस दृष्टिकोण के साथ मुख्य समस्याएं निम्नलिखित हैं:

  • जब नई उपप्रकारों को जोड़ने की बात आती है, तो आपको इन नई वस्तुओं का वर्णन करने वाली विशेषताओं को समायोजित करने के लिए तालिका को बदलना होगा। जब आपके पास कई उपप्रकार होते हैं, या यदि आप नियमित आधार पर उपप्रकारों को जोड़ने की योजना बनाते हैं, तो यह जल्दी समस्याग्रस्त हो सकता है।

  • डेटाबेस लागू नहीं कर पाएगा कि कौन सी विशेषताएँ लागू होती हैं और कौन सी नहीं, क्योंकि कोई मेटाडेटा यह निर्धारित करने के लिए नहीं है कि कौन सी विशेषताएँ किस उप-वर्ग से संबंधित हैं।

  • आप NOT NULLएक उप-प्रकार की विशेषताओं पर भी लागू नहीं कर सकते हैं जो अनिवार्य होनी चाहिए। आपको इसे अपने एप्लिकेशन में संभालना होगा, जो सामान्य रूप से आदर्श नहीं है।

कंक्रीट टेबल विरासत:

विरासत से निपटने के लिए एक और दृष्टिकोण प्रत्येक उपप्रकार के लिए एक नई तालिका बनाना है, प्रत्येक तालिका में सभी सामान्य विशेषताओं को दोहराते हुए। उदाहरण के लिए:

--// Table: policies_motor
+------+---------------------+----------------+
| id   | date_issued         | vehicle_reg_no |
+------+---------------------+----------------+
|    1 | 2010-08-20 12:00:00 | 01-A-04004     |
|    2 | 2010-08-20 13:00:00 | 02-B-01010     |
|    3 | 2010-08-20 15:00:00 | 03-C-02020     |
+------+---------------------+----------------+
                          
--// Table: policies_property    
+------+---------------------+------------------+
| id   | date_issued         | property_address |
+------+---------------------+------------------+
|    1 | 2010-08-20 14:00:00 | Oxford Street    |   
+------+---------------------+------------------+

यह डिज़ाइन मूल रूप से एकल तालिका पद्धति के लिए पहचानी गई समस्याओं को हल करेगी:

  • अनिवार्य विशेषताओं को अब लागू किया जा सकता है NOT NULL

  • एक नया उपप्रकार जोड़ने के लिए एक मौजूदा एक में कॉलम जोड़ने के बजाय एक नई तालिका जोड़ना आवश्यक है।

  • इसमें कोई जोखिम नहीं है कि किसी विशेष उप-प्रकार के लिए एक अनुचित विशेषता सेट की गई है, जैसे कि vehicle_reg_noसंपत्ति नीति के लिए फ़ील्ड।

  • typeएकल तालिका पद्धति में विशेषता के लिए कोई आवश्यकता नहीं है । प्रकार अब मेटाडेटा द्वारा परिभाषित किया गया है: तालिका नाम।

हालांकि यह मॉडल कुछ नुकसान के साथ भी आता है:

  • सामान्य विशेषताओं को उपप्रकार विशिष्ट विशेषताओं के साथ मिलाया जाता है, और उन्हें पहचानने का कोई आसान तरीका नहीं है। डेटाबेस या तो पता नहीं चलेगा।

  • तालिकाओं को परिभाषित करते समय, आपको प्रत्येक उपप्रकार तालिका के लिए सामान्य विशेषताओं को दोहराना होगा। यह निश्चित रूप से नहीं है DRY

  • उपप्रकार की परवाह किए बिना सभी नीतियों के लिए खोज करना मुश्किल हो जाता है, और इसके लिए एक समूह की आवश्यकता होगी UNION

इस प्रकार से आपको सभी नीतियों को क्वेरी करना होगा:

SELECT     date_issued, other_common_fields, 'MOTOR' AS type
FROM       policies_motor
UNION ALL
SELECT     date_issued, other_common_fields, 'PROPERTY' AS type
FROM       policies_property;

ध्यान दें कि नए उप-योगों को जोड़ने के UNION ALLलिए प्रत्येक उप-प्रकार के लिए एक अतिरिक्त के साथ संशोधित करने के लिए उपरोक्त क्वेरी की आवश्यकता होगी । यदि यह ऑपरेशन भूल जाता है तो यह आपके एप्लिकेशन में आसानी से बग पैदा कर सकता है।

क्लास टेबल इनहेरिटेंस (उर्फ टेबल प्रति टाइप इनहेरिटेंस):

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

CREATE TABLE policies (
   policy_id          int,
   date_issued        datetime,

   -- // other common attributes ...
);

CREATE TABLE policy_motor (
    policy_id         int,
    vehicle_reg_no    varchar(20),

   -- // other attributes specific to motor insurance ...

   FOREIGN KEY (policy_id) REFERENCES policies (policy_id)
);

CREATE TABLE policy_property (
    policy_id         int,
    property_address  varchar(20),

   -- // other attributes specific to property insurance ...

   FOREIGN KEY (policy_id) REFERENCES policies (policy_id)
);

यह समाधान अन्य दो डिज़ाइनों में पहचानी गई समस्याओं को हल करता है:

  • अनिवार्य विशेषताओं के साथ लागू किया जा सकता है NOT NULL

  • एक नया उपप्रकार जोड़ने के लिए एक मौजूदा एक में कॉलम जोड़ने के बजाय एक नई तालिका जोड़ना आवश्यक है।

  • कोई जोखिम नहीं है कि एक अनुचित विशेषता एक विशेष उपप्रकार के लिए निर्धारित है।

  • typeविशेषता के लिए कोई ज़रूरत नहीं है ।

  • अब आम विशेषताओं को उप-प्रकार विशिष्ट विशेषताओं के साथ अब और नहीं मिलाया जाता है।

  • हम अंत में DRY रह सकते हैं। तालिकाओं को बनाते समय प्रत्येक उपप्रकार तालिका के लिए सामान्य विशेषताओं को दोहराने की आवश्यकता नहीं है।

  • idनीतियों के लिए एक ऑटो वेतन वृद्धि का प्रबंधन करना आसान हो जाता है, क्योंकि इसे स्वतंत्र रूप से उत्पन्न करने वाली प्रत्येक उपप्रकार तालिका के बजाय बेस टेबल द्वारा नियंत्रित किया जा सकता है।

  • उपप्रकार की परवाह किए बिना सभी नीतियों के लिए खोज करना अब बहुत आसान हो गया है: कोई UNIONज़रूरत नहीं है - बस एक SELECT * FROM policies

मैं क्लास टेबल के दृष्टिकोण को ज्यादातर स्थितियों में सबसे उपयुक्त मानता हूं।


इन तीनों मॉडलों के नाम मार्टिन फाउलर की पुस्तक पैटर्न ऑफ एंटरप्राइज एप्लीकेशन आर्किटेक्चर से आए हैं ।


97
मैं भी इस डिजाइन का उपयोग कर रहा हूं, लेकिन आपने कमियों का उल्लेख नहीं किया है। विशेष रूप से: 1) आप कहते हैं कि आपको प्रकार की आवश्यकता नहीं है; सच है, लेकिन आप एक पंक्ति के वास्तविक प्रकार की पहचान नहीं कर सकते जब तक कि आप एक मैच खोजने के लिए सभी उपप्रकार तालिकाओं को न देखें। 2) मास्टर टेबल और उप-प्रकार तालिकाओं को सिंक में रखना कठिन है (कोई भी उप-तालिका में पंक्ति हटा सकता है और मास्टर तालिका में नहीं)। 3) आपके पास प्रत्येक मास्टर पंक्ति के लिए एक से अधिक उपप्रकार हो सकते हैं। मैं 1 के आसपास काम करने के लिए ट्रिगर्स का उपयोग करता हूं, लेकिन 2 और 3 बहुत कठिन समस्याएं हैं। वास्तव में 3 एक समस्या नहीं है यदि आप संरचना बनाते हैं, लेकिन सख्त विरासत के लिए है।

19
+1 @ टिबो की टिप्पणी के लिए, यह एक गंभीर समस्या है। कक्षा तालिका विरासत वास्तव में एक असामान्य स्कीमा का उत्पादन करती है। जहां कंक्रीट टेबल इनहेरिटेंस नहीं है, और मैं इस तर्क से सहमत नहीं हूं कि कंक्रीट टेबल इनहेरिटेंस DRY में बाधा डालता है। SQL DRY में बाधा डालता है, क्योंकि इसमें मेटाप्रोग्रामिंग की कोई सुविधा नहीं है। समाधान यह है कि डेटाबेस टूलकिट का उपयोग करें (या अपना खुद का लिखना) भारी उठाने के लिए, एसक्यूएल को सीधे लिखने के बजाय (याद रखें, यह वास्तव में केवल एक डीबी इंटरफ़ेस भाषा है)। सब के बाद, आप विधानसभा में अपने उद्यम आवेदन भी नहीं लिखते हैं।
Jo So

18
@ टिबो, पॉइंट 3 के बारे में, आप यहाँ बताए गए दृष्टिकोण का उपयोग कर सकते हैं: sqlteam.com/article/… , मॉडलिंग वन-टू- एअर कन्सट्रक्शन सेक्शन की जाँच करें।
एंड्रयू

4
@DanielVassallo सबसे पहले आश्चर्यजनक जवाब के लिए धन्यवाद, 1 संदेह यदि किसी व्यक्ति के पास पॉलिसी है तो यह कैसे पता करें कि उसकी पॉलिसी_माटर या पॉलिसी_प्रॉपर्टी है? एक तरीका यह है कि सभी सब टेबल्स में पॉलिसी को खोजा जाए, लेकिन मुझे लगता है कि यह खराब तरीका है, यह सही तरीका नहीं है?
थॉमस बेकर

11
मुझे वास्तव में आपका तीसरा विकल्प पसंद है। हालाँकि, मैं उलझन में हूँ कि SELECT कैसे काम करेगा। यदि आप नीतियों का चयन करते हैं, तो आपको पॉलिसी आईडी वापस मिल जाएगी, लेकिन आपको अभी भी यह नहीं पता होगा कि पॉलिसी किस उपप्रकार तालिका से संबंधित है। क्या अब भी आपको सभी पॉलिसी विवरण प्राप्त करने के लिए सभी उपप्रकारों के साथ एक JOIN नहीं करना होगा?
आदम

14

तीसरा विकल्प "नीति" तालिका बनाना है, फिर एक "सेक्शनमैन" तालिका है जो उन सभी क्षेत्रों को संग्रहीत करती है जो सभी प्रकार के वर्गों में आम हैं। फिर प्रत्येक प्रकार के अनुभाग के लिए अन्य तालिकाओं का निर्माण करें जिसमें केवल वे फ़ील्ड शामिल हों जो सामान्य नहीं हैं।

निर्णय लेना जो सबसे अच्छा है, वह ज्यादातर इस बात पर निर्भर करता है कि आपके पास कितने क्षेत्र हैं और आप अपनी SQL कैसे लिखना चाहते हैं। वे सब काम करेंगे। यदि आपके पास कुछ क्षेत्र हैं तो मैं शायद # 1 के साथ जाऊंगा। खेतों के "लॉट" के साथ मैं # 2 या # 3 की ओर झुक जाऊंगा।


+1: तीसरा विकल्प वंशानुक्रम मॉडल के सबसे करीब है, और सबसे सामान्यीकृत IMO
RedFilter

आपका विकल्प # 3 वास्तव में सिर्फ # विकल्प 2 से मेरा मतलब है। कई क्षेत्र हैं और कुछ खंड में बाल संस्थाएँ भी होंगी।
स्टीव जोंस

9

दी गई जानकारी के साथ, मैं निम्नलिखित के लिए डेटाबेस मॉडल करूँगा:

नीतियों

  • POLICY_ID (प्राथमिक कुंजी)

देयताएं

  • LIABILITY_ID (प्राथमिक कुंजी)
  • POLICY_ID (विदेशी कुंजी)

गुण

  • PROPERTY_ID (प्राथमिक कुंजी)
  • POLICY_ID (विदेशी कुंजी)

... और इसी तरह, क्योंकि मुझे उम्मीद है कि नीति के प्रत्येक अनुभाग के साथ अलग-अलग विशेषताएं होंगी। अन्यथा, एक एकल SECTIONSतालिका हो सकती है और इसके अलावा policy_id, वहाँ एक section_type_code...

किसी भी तरह से, यह आपको प्रति नीति वैकल्पिक वर्गों का समर्थन करने की अनुमति देगा ...

मुझे समझ नहीं आ रहा है कि आप इस दृष्टिकोण के बारे में क्या असंतोषजनक पाते हैं - यह है कि कैसे आप संदर्भ अखंडता बनाए रखते हुए डेटा संग्रहीत करते हैं और डेटा की नकल नहीं करते हैं। शब्द "सामान्यीकृत" है ...

क्योंकि SQL SET आधारित है, यह प्रक्रियात्मक / OO प्रोग्रामिंग अवधारणाओं के लिए विदेशी है और एक दायरे से दूसरे में संक्रमण के लिए कोड की आवश्यकता है। ओआरएम को अक्सर माना जाता है, लेकिन वे उच्च मात्रा, जटिल प्रणालियों में अच्छी तरह से काम नहीं करते हैं।


हाँ, मुझे सामान्यीकरण की बात मिलती है ;-) ऐसी जटिल संरचना के लिए, कुछ वर्गों के सरल होने के साथ ही कुछ की अपनी जटिल उप-संरचना होती है, ऐसा लगता नहीं है कि कोई ORM काम करेगा, हालाँकि यह अच्छा होगा।
स्टीव जोंस

6

डैनियल वेसलो समाधान के अतिरिक्त, यदि आप SQL सर्वर 2016+ का उपयोग करते हैं, तो एक और समाधान है जो मैंने कुछ मामलों में प्रदर्शन के काफी खोए बिना उपयोग किया था।

आप केवल सामान्य फ़ील्ड के साथ एक तालिका बना सकते हैं और JSON स्ट्रिंग के साथ एक एकल कॉलम जोड़ सकते हैं जिसमें सभी उपप्रकार विशिष्ट फ़ील्ड शामिल हैं।

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


1
यह एक दिलचस्प विचार है। मैंने JSON का उपयोग SQL सर्वर में अभी तक नहीं किया है, लेकिन इसे कहीं और उपयोग करें। सर उठाने के लिए धन्यवाद।
स्टीव जोन्स

5

इसे करने का दूसरा तरीका, INHERITSघटक का उपयोग कर रहा है। उदाहरण के लिए:

CREATE TABLE person (
    id int ,
    name varchar(20),
    CONSTRAINT pessoa_pkey PRIMARY KEY (id)
);

CREATE TABLE natural_person (
    social_security_number varchar(11),
    CONSTRAINT pessoaf_pkey PRIMARY KEY (id)
) INHERITS (person);


CREATE TABLE juridical_person (
    tin_number varchar(14),
    CONSTRAINT pessoaj_pkey PRIMARY KEY (id)
) INHERITS (person);

इस प्रकार तालिकाओं के बीच एक विरासत को परिभाषित करना संभव है।


PostgreSQL केINHERITS अलावा अन्य DBs का समर्थन करता है ? उदाहरण के लिए MySQL ?
जिआनीस क्रिस्तोफिस

1
@giannischristofakis: MySQL केवल एक रिलेशनल डेटाबेस है, जबकि Postgres एक ऑब्जेक्ट-रिलेशनल डेटाबेस है। इसलिए, कोई भी MySQL इसका समर्थन नहीं करता है। वास्तव में, मुझे लगता है कि पोस्टग्रैज एकमात्र चालू डीबीएमएस है जो इस प्रकार की विरासत का समर्थन करता है।
a_horse_with_no_name

2
@ marco-paulo-ollivier, ओपी का सवाल SQL सर्वर के बारे में है, इसलिए मुझे समझ नहीं आता कि आप एक समाधान क्यों प्रदान करते हैं जो केवल पोस्टग्रेज के साथ काम करता है। जाहिर है, समस्या का समाधान नहीं।
मप्तो

@mapto यह प्रश्न "डेटाबेस में किसी ओओ स्टाइल इनहेरिटेंस कैसे करता है" के कुछ लक्ष्य हो गए हैं; यह मूल रूप से sql सर्वर के बारे में था जो अब अप्रासंगिक है
कायुस जार्ड

0

मैं विधि # 1 (एक एकीकृत अनुभाग तालिका) की ओर झुकता हूं, अपने सभी वर्गों के साथ पूरी नीतियों को कुशलतापूर्वक प्राप्त करने के लिए (जो मुझे लगता है कि आपकी प्रणाली बहुत कुछ कर रही होगी)।

इसके अलावा, मुझे नहीं पता कि आप SQL सर्वर के किस संस्करण का उपयोग कर रहे हैं, लेकिन 2008+ स्पार्स कॉलम में उन स्थितियों में प्रदर्शन को अनुकूलित करने में मदद मिलती है, जहां एक कॉलम में कई मान NULL होंगे।

अंत में, आपको यह तय करना होगा कि नीति अनुभाग कैसे "समान" हैं। जब तक वे पर्याप्त रूप से भिन्न नहीं हो जाते, मुझे लगता है कि अधिक सामान्यीकृत समाधान से अधिक परेशानी हो सकती है ... लेकिन केवल आप ही उस कॉल को कर सकते हैं। :)


संपूर्ण नीति को एक बार में प्रस्तुत करने के लिए बहुत अधिक जानकारी होगी, इसलिए पूरे रिकॉर्ड को पुनः प्राप्त करना कभी भी आवश्यक नहीं होगा। मुझे लगता है कि यह 2005 है, हालांकि मैंने अन्य परियोजनाओं में 2008 के विरल का उपयोग किया है।
स्टीव जोंस

शब्द "यूनिफाइड सेक्शन टेबल" कहां से आ रहा है? Google इसके लिए लगभग कोई परिणाम नहीं दिखाता है और यहां पहले से ही काफी भ्रमित करने वाले शब्द हैं।
Stephan-v

-1

वैकल्पिक रूप से, एक दस्तावेज़ डेटाबेस (जैसे MongoDB) का उपयोग करने पर विचार करें जो मूल रूप से समृद्ध डेटा संरचनाओं और नेस्टिंग का समर्थन करता है।


हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.