क्या कई विदेशी कुंजियों का उपयोग अल्पविराम द्वारा अलग किया गया है, और यदि हां, तो क्यों?


31

दो टेबल हैं: Dealऔर DealCategories। एक सौदे में कई सौदा श्रेणियां हो सकती हैं।

तो DealCategoriesनिम्नलिखित संरचना के साथ एक तालिका बनाने के लिए उचित तरीका होना चाहिए :

DealCategoryId (PK)
DealId (FK)
DealCategoryId (FK)

हालाँकि, हमारी आउटसोर्स टीम ने कई श्रेणियों को Dealइस प्रकार संग्रहीत किया है :

DealId (PK)
DealCategory -- In here they store multiple deal ids separated by commas like this: 18,25,32.

मुझे लगता है कि उन्होंने जो किया वह गलत है, लेकिन मुझे नहीं पता कि यह स्पष्ट रूप से कैसे समझाया जाए कि यह सही नहीं है।

मैं उन्हें कैसे समझाऊँ कि यह गलत है? या शायद मैं वही हूँ जो गलत है और यह स्वीकार्य है?



7
इससे पहले कि वे किसी भी अधिक नुकसान करते हैं, टीम ने आग लगा दी ... (-_-)
राफा

जवाबों:


49

हाँ यह एक भयानक विचार है।

जाने के बजाय:

SELECT Deal.Name, DealCategory.Name
FROM Deal
  INNER JOIN
     DealCategories ON Deal.DealID = DealCategories.DealID
  INNER JOIN
     DealCategory ON DealCategories.DealCategoryID = DealCategory.DealCategoryID
WHERE Deal.DealID = 1234

अब आपको जाना है:

SELECT Deal.ID, Deal.Name, DealCategories
FROM Deal
WHERE Deal.DealID = 1234

फिर आपको अपने कॉम्प्लेक्स कोड को अलग-अलग संख्याओं में विभाजित करने के लिए अपने एप्लिकेशन कोड में सामान करने की आवश्यकता है, फिर डेटाबेस को अलग-अलग क्वेरी करें:

SELECT DealCategory.Name
FROM DealCategory
WHERE DealCategory.DealCategoryID IN (<<that list from before>>)

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

SQL Antipatterns (Karwin, 2010) इस एंटीपैर्टन (जिसे वह 'Jaywalking' कहते हैं) के एक पूरे अध्याय को समर्पित करता है, पृष्ठ 15-23। इसके अलावा, लेखक ने SO पर एक समान प्रश्न पर पोस्ट किया है । मुख्य बिंदु वह नोट करते हैं (जैसा कि इस उदाहरण पर लागू होता है) हैं:

  • एक विशिष्ट श्रेणी में सभी सौदों के लिए क्वेरी करना जटिल है (उस समस्या को हल करने का सबसे आसान तरीका एक नियमित अभिव्यक्ति है, लेकिन एक नियमित अभिव्यक्ति अपने आप में एक समस्या है)।
  • आप विदेशी कुंजी संबंधों के बिना संदर्भ अखंडता को लागू नहीं कर सकते। यदि आप DealCategory nr को हटाते हैं। # 26, आप तब, अपने आवेदन कोड में, प्रत्येक सौदे के माध्यम से जाने के लिए श्रेणी # 26 के संदर्भ की तलाश में हैं और उन्हें हटा दें। यह एक ऐसी चीज है जिसे डेटा लेयर पर संभाला जाना चाहिए, और इसे अपने एप्लिकेशन में संभालना बहुत बुरी बात है
  • अलग-अलग प्रश्न ( COUNT, SUMआदि), फिर से, 'जटिल' से 'लगभग असंभव' तक भिन्न होते हैं। अपने डेवलपर्स से पूछें कि वे आपको उस श्रेणी के सौदों की संख्या की गणना के साथ सभी श्रेणियों की सूची कैसे प्राप्त करेंगे। एक उचित डिजाइन के साथ, यह SQL की चार लाइनें है।
  • अपडेट अधिक कठिन हो जाते हैं (अर्थात आपके पास पांच श्रेणियों में एक सौदा है, लेकिन आप दो को निकालना चाहते हैं और तीन अन्य को जोड़ना चाहते हैं)। कि उचित डिजाइन के साथ SQL की तीन लाइनें हैं।
  • आखिरकार आप VARCHARसूची की लंबाई सीमाओं में भाग लेंगे । हालाँकि अगर आपके पास 4000 वर्णों से अधिक अल्पविराम वाली सूची है, तो संभावना है कि राक्षस वैसे भी नरक के रूप में धीमा होने वाला है।
  • डेटाबेस से एक सूची खींचना, इसे अलग करना, और फिर किसी अन्य क्वेरी के लिए डेटाबेस में वापस जाना आंतरिक रूप से एक क्वेरी की तुलना में धीमा है।

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


1
साइमन, किसी ने एक ही सवाल किया ( dba.stackexchange.com/questions/17824/… ), लेकिन मुझे यह स्पष्ट नहीं है कि एक ही FK और PK एक ही तालिका में क्यों हैं, 3FN को ब्रेक दें।
jcho360

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

4

हालाँकि, हमारी आउटसोर्स टीम ने डील टेबल में कई श्रेणियों को इस तरह संग्रहीत किया है:

DealId (PK) DealCategory - यहाँ वे इस तरह से अल्पविराम द्वारा अलग किए गए कई सौदा आईडी संग्रहीत करते हैं: 18,25,32।

यह वास्तव में एक अच्छा डिज़ाइन है यदि आपको केवल किसी दिए गए सौदे के लिए श्रेणियों के लिए क्वेरी करने की आवश्यकता है।

लेकिन यह बहुत ही भयानक है अगर आप किसी श्रेणी में सभी सौदे जानना चाहते हैं।

और यह वास्तव में कुछ भी करने के लिए कठिन और त्रुटि-प्रवण बनाता है - जैसे कि अपडेट, काउंट, जॉइन, आदि।

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

अनुकूलन के किसी भी अन्य रूप की तरह, आपको यह जानना होगा कि यदि आप यह तय कर सकते हैं कि क्या समस्याएँ चल रही हैं, तो क्या होगा।


1
क्या आपको वास्तव में लगता है कि अल्पविराम से अलग किया गया बच्चा आईडी एक स्ट्रिंग सहायक है? मेरा मतलब है, आवेदन को पहले पढ़ना था, फिर आईडी को पार्स करना और सभी बच्चों को क्वेरी करना, जैसे select * from DealCategories where DealId in (1,2,3,4,...)। आपके पास डेटाबेस डिज़ाइन के बारे में मुझसे अधिक अनुभव है, इसलिए हो सकता है कि बहुत विशिष्ट मामलों में इस तरह के "चरम ट्यूनिंग" के लिए कुछ मामलों में आपके पास अच्छा कारण हो । selectइसको सही ठहराने का मेरा एकमात्र विचार डील / डील कैटररी पर बहुत अधिक भार है। यह मुझे किसी भी डीबी डिजाइन के बिना कुछ आउटसोर्स टीम की तरह दिखता है, टेबल बनाने से परे, इसे बनाया।
एरिक हार्ट

1
@ एरिकहार्ट, यह विकृति है, और यह सहायक हो सकता है, लेकिन मेरा कहना यह है कि यह पूरी तरह से उन प्रश्नों पर निर्भर करता है जिन्हें आपको चलाने की आवश्यकता है। आप सही कह रहे हैं कि सभी प्रश्नों को बदतर बनाने के लिए वियोग का प्रदर्शन खराब होता है, सिवाय एक क्वेरी के जो इसके लिए अनुकूलन करता है। यदि आपको केवल उस एक क्वेरी को चलाने की आवश्यकता है, और आप अन्य प्रश्नों के बारे में परवाह नहीं करते हैं, तो यह एक जीत है। लेकिन ये दुर्लभ मामले हैं, क्योंकि आमतौर पर हम डेटा को विभिन्न तरीकों से क्वेरी करना चाहते हैं।
बिल करविन

1
@ एरिकहार्ट, यदि उस आउटसोर्स टीम को परियोजना विनिर्देश दिए गए थे जिसमें इस डेटा के खिलाफ केवल एक क्वेरी शामिल थी, तो वे केवल उस विशिष्ट क्वेरी के लिए एक अनुकूलन डिज़ाइन कर सकते थे। दूसरे शब्दों में, "आपने इसके लिए कहा, आपको मिल गया।" लेकिन आउटसोर्सिंग प्रदाता के पास डेटा के भविष्य के उपयोग की योजना बनाने का कोई कारण नहीं है - वे आवेदन को उस पत्र के लिए लागू करते हैं जो कल्पना में लिखा गया है।
बिल कारविन

1

एक कॉलम में कई मान 1 सामान्य रूप के खिलाफ हैं।

यह भी बिल्कुल गति नहीं है, क्योंकि तालिका को डेटाबेस में जोड़ा जाना है। आपको पहले एक स्ट्रिंग पढ़ना और पार्स करना है, फिर "डील" के लिए सभी श्रेणियों का चयन करें।

सही क्रियान्वयन एक सौदा सारणी होगी जैसे "DealDealCategories", DealId और DealCategoryId के साथ।

बुरा पदानुक्रम कार्यान्वयन?

इसके अलावा, DealCategory में एक और DealCategory के लिए FK, DealCategory के एक पदानुक्रम / ट्री के खराब कार्यान्वयन की तरह दिखता है। एक माता-पिता आईडी (तथाकथित आसन्न सूची) के माध्यम से पेड़ों के साथ काम करना एक दर्द है!

नेस्टेड सेट (पढ़ने के लिए अच्छा है, लेकिन संशोधित करने के लिए कठिन) और क्लोजिंग टेबल्स (सबसे अच्छा समग्र प्रदर्शन, लेकिन संभवतः उच्च मेमोरी उपयोग - शायद आपके डीटेलसीरीज के लिए बहुत ज्यादा नहीं) को पदानुक्रम लागू करते समय चेक करें!

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