डेटाबेस डिजाइन: कैसे "संग्रह" समस्या को संभालने के लिए?


18

मुझे पूरा यकीन है कि बहुत सारे एप्लिकेशन, महत्वपूर्ण एप्लिकेशन, बैंक और ऐसा दैनिक आधार पर करते हैं।

सब के पीछे विचार है:

  • सभी पंक्तियों का एक इतिहास होना चाहिए
  • सभी लिंक सुसंगत रहना चाहिए
  • "वर्तमान" कॉलम प्राप्त करने के लिए अनुरोध करना आसान होना चाहिए
  • जिन ग्राहकों ने अप्रचलित चीजें खरीदी हैं, उन्हें अभी भी यह देखना चाहिए कि उन्होंने क्या खरीदा है भले ही यह उत्पाद कैटलॉग का हिस्सा नहीं है

और इसी तरह।

यहाँ मैं क्या करना चाहता हूँ, और मैं उन समस्याओं की व्याख्या करूँगा जो मैं कर रहा हूँ।

मेरे सभी टेबल में वे कॉलम होंगे:

  • id
  • id_origin
  • date of creation
  • start date of validity
  • start end of validity

और यहाँ CRUD संचालन के लिए विचार हैं:

  • बनाएँ = के साथ नई पंक्ति डालें id_origin= id, date of creation= अब, start date of validity= अब, end date of validity= शून्य (= इसका मतलब है कि यह वर्तमान सक्रिय रिकॉर्ड है)
  • अद्यतन =
    • read end date of validity== सभी रिकॉर्ड्स को == null के साथ पढ़ें
    • "वर्तमान" रिकॉर्ड end date of validity= के साथ null end date of validity= अभी अपडेट करें
    • नए मानों के साथ एक नया बनाएं, और end date of validity= null (= इसका मतलब यह वर्तमान सक्रिय रिकॉर्ड है)
  • हटाना = "वर्तमान" रिकॉर्ड को अपडेट end date of validity= साथ अशक्त end date of validityअब =

तो यहाँ मेरी समस्या है: कई-से-कई संघों के साथ। मानों के साथ एक उदाहरण लेते हैं:

  • तालिका A (id = 1, id_origin = 1, start = now, end = null)
  • तालिका A_B (प्रारंभ = अब, अंत = शून्य, id_A = 1, id_B = 48)
  • तालिका B (id = 48, id_origin = 48, start = now, end = null)

अब मैं तालिका ए, रिकॉर्ड आईडी = 1 को अपडेट करना चाहता हूं

  • मैं रिकॉर्ड आईडी = 1 को अंत के साथ चिह्नित करता हूं
  • मैं तालिका A और ... में एक नया मान सम्मिलित करता हूं ... धिक्कार है कि मैंने अपना संबंध A_B खो दिया है, जब तक कि मैं संबंध की नकल नहीं करता, तब तक ... यह तालिका में समाप्त हो जाएगा:

  • तालिका A (id = 1, id_origin = 1, start = now, end = now + 8mn)

  • तालिका A (id = 2, id_origin = 1, start = now + 8mn, end = null)
  • तालिका A_B (प्रारंभ = अब, अंत = शून्य, id_A = 1, id_B = 48)
  • तालिका A_B (प्रारंभ = अब, अंत = शून्य, id_A = 2, id_B = 48)
  • तालिका B (id = 48, id_origin = 48, start = now, end = null)

और ... अच्छी तरह से मुझे एक और समस्या है: संबंध A_B: क्या मैं (id_A = 1, id_B = 48) को अप्रचलित के रूप में चिह्नित करूंगा या नहीं (A - id = 1 अप्रचलित है, लेकिन B - 48 नहीं)?

इससे कैसे निपटें?

मुझे इसे बड़े पैमाने पर डिजाइन करना है: उत्पाद, साझेदार, और इसी तरह।

इस पर आपका क्या अनुभव है? आप कैसे करेंगे (आपने कैसे किया है)?

- संपादित करें

मुझे यह बहुत दिलचस्प लेख मिला है , लेकिन यह "कैस्केडिंग किशोरावस्था" के साथ ठीक से व्यवहार नहीं करता है (= जो मैं वास्तव में पूछ रहा हूं)


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

बल्कि उस पहिये का फिर से आविष्कार करने के लिए, क्या आपने उपयोग करने पर विचार किया है, उदाहरण के लिए, ओरेकल पर फ्लैशबैक डेटा आर्काइव ?
जैक डगलस

जवाबों:


4

यह मेरे लिए स्पष्ट नहीं है अगर ये आवश्यकताएं ऑडिटिंग उद्देश्यों या केवल सरल ऐतिहासिक संदर्भ जैसे सीआरएम और शॉपिंग कार्ट के लिए हैं।

किसी भी तरह से, विचार करें कि प्रत्येक प्रमुख क्षेत्र के लिए एक मुख्य और main_archive तालिका है जहां यह आवश्यक है। "मुख्य" में केवल वर्तमान / सक्रिय प्रविष्टियाँ होंगी जबकि "main_archive" में उन सभी चीज़ों की प्रतिलिपि होगी जो कभी भी मुख्य में जाती हैं। सम्मिलित करें / अपडेट करें main_archive मुख्य में सम्मिलित करें / अपडेट से ट्रिगर हो सकता है। Main_archive के खिलाफ डिलीट तब और अधिक समय तक चल सकता है, यदि कभी हो।

संदर्भ मुद्दों जैसे कि Cust X ने उत्पाद Y को खरीदा, cust_archive -> product_archive की आपकी संदर्भात्मक चिंता को हल करने का सबसे आसान तरीका है कि product_archive से प्रविष्टियों को कभी न हटाएं। आम तौर पर, उस तालिका में मंथन बहुत कम होना चाहिए, इसलिए आकार चिंता का बहुत बुरा नहीं होना चाहिए।

HTH।


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

1
अधिकांश डेटाबेस में मैं डिज़ाइन करता हूं सभी 'मुख्य' तालिकाओं में उत्पाद नाम का एक उपसर्ग होता है LP_, और हर एक महत्वपूर्ण तालिका में एक समतुल्य होता है LH_, जिसमें ट्रिगर, डालने, अद्यतन, हटाने पर ऐतिहासिक पंक्तियों को सम्मिलित करता है। यह सभी मामलों के लिए काम नहीं करता है, लेकिन यह उन चीजों के लिए एक ठोस मॉडल है जो मैं करता हूं।

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

1
@onupdatecascade: ध्यान दें कि (कम से कम कुछ RDBMS में) आप उस UNIONदृश्य पर सूचकांक डाल सकते हैं, जो आपको वर्तमान और ऐतिहासिक रिकॉर्ड दोनों में एक अद्वितीय बाधा को लागू करने जैसी ठंडी चीजें करने देता है।
जॉन ऑफ ऑल ट्रेड्स

5 साल बाद, मैंने कई चीजें की हैं और हर समय मैं आपको अपना विचार वापस देता हूं। केवल एक चीज जो मैंने बदली, वह है इतिहास की तालिकाओं पर, मेरे पास एक कॉलम " id", और " id_ref" है। id_refतालिका के वास्तविक विचार का संदर्भ है। उदाहरण: personऔर person_h। में person_hमैं "है id", और " id_ref" जहां id_ref'से संबंधित है person.id' तो मैं एक ही साथ कई पंक्तियों हो सकता है person.id(= के एक पंक्ति जब personसंशोधित किया गया है) और सभी id'मेरे सभी तालिकाओं की है autoinc हैं।
ओलिवियर पोंस

2

यह कार्यात्मक प्रोग्रामिंग के साथ कुछ ओवरलैप है; विशेष रूप से अपरिवर्तनीयता की अवधारणा।

आपके पास एक टेबल है PRODUCTऔर दूसरा कॉल PRODUCTVERSIONया समान है। जब आप किसी उत्पाद को बदलते हैं तो आप अपडेट नहीं करते हैं, आप बस एक नई PRODUCTVERSIONपंक्ति सम्मिलित करते हैं । नवीनतम प्राप्त करने के लिए, आप तालिका को संस्करण संख्या (desc), टाइमस्टैम्प (desc) द्वारा अनुक्रमणित कर सकते हैं, या आपके पास एक ध्वज ( LatestVersion) हो सकता है।

अब यदि आपके पास कोई ऐसी चीज़ है जो किसी उत्पाद को संदर्भित करती है, तो आप यह तय कर सकते हैं कि वह किस तालिका की ओर इशारा करती है। क्या यह PRODUCTइकाई (हमेशा इस उत्पाद को संदर्भित करता है) या PRODUCTVERSIONइकाई को इंगित करता है (केवल उत्पाद के इस संस्करण को संदर्भित करता है)?

यह जटिल हो जाता है। यदि आपके पास उत्पाद की तस्वीरें हैं तो क्या होगा? उन्हें संस्करण तालिका की ओर इशारा करना होगा, क्योंकि उन्हें बदला जा सकता है, लेकिन कई मामलों में, वे नहीं करेंगे और आप अनावश्यक रूप से डेटा की नकल नहीं करना चाहते हैं। इसका मतलब है कि आपको एक PICTUREटेबल और PRODUCTVERSIONPICTUREकई-कई संबंधों की आवश्यकता है।


1

मैंने 4 क्षेत्रों के साथ यहां से सभी सामानों को लागू किया है जो मेरी सभी तालिकाओं पर हैं:

  • आईडी
  • date_creation
  • date_validity_start
  • date_validity_end

हर बार एक रिकॉर्ड को संशोधित करना पड़ता है, मैं इसे डुप्लिकेट करता हूं, डुप्लिकेट किए गए रिकॉर्ड को "पुराना" = date_validity_end=NOW()और वर्तमान को अच्छे के रूप में चिह्नित करता हूं date_validity_start=NOW()और date_validity_end=NULL

चाल कई के बारे में कई और एक से कई संबंधों के बारे में है: यह उन्हें छूने के बिना काम करता है! यह उन सभी प्रश्नों के बारे में है जो अधिक जटिल हैं: एक सटीक तारीख (= अभी नहीं) में एक रिकॉर्ड को क्वेरी करने के लिए , मुझे प्रत्येक बाधा के लिए, और मुख्य तालिका के लिए, उन बाधाओं को जोड़ने के लिए है:

WHERE (
  (date_validity_start<=:dateparam AND date_validity_end IS NULL)
  OR
  (date_validity_start<=:dateparam AND date_validity_start>=:dateparam)
)

उत्पादों और विशेषताओं के साथ (कई संबंध के लिए):

SELECT p.*,a.*

FROM products p

JOIN products_attributes pa
ON pa.id_product = p.id
AND (
  (pa.date_validity_start<=:dateparam AND pa.date_validity_end IS NULL)
  OR
  (pa.date_validity_start<=:dateparam AND pa.date_validity_start>=:dateparam)
)

JOIN attributes a
ON a.id = pa.id_attribute
AND (
  (a.date_validity_start<=:dateparam AND a.date_validity_end IS NULL)
  OR
  (a.date_validity_start<=:dateparam AND a.date_validity_start>=:dateparam)
)

WHERE (
  (p.date_validity_start<=:dateparam AND p.date_validity_end IS NULL)
  OR
  (p.date_validity_start<=:dateparam AND p.date_validity_start>=:dateparam)
)

0

इस बारे में कैसा है? अतीत में मैंने जो कुछ किया है, उसके लिए यह सरल और बहुत प्रभावी लगता है। अपने "इतिहास" तालिका में, एक अलग पीके का उपयोग करें। तो, आपका "CustomerID" फ़ील्ड आपकी ग्राहक तालिका में PK है, लेकिन "इतिहास" तालिका में, आपका PK "NewCustomerID" है। "CustomerID" केवल एक और रीड-ओनली फ़ील्ड बन जाता है। यह "CustomerID" को इतिहास में अपरिवर्तित छोड़ देता है और आपके सभी रिश्ते बरकरार रहते हैं।


बहुत अच्छा विचार है। मैंने जो किया है वह बहुत समान है: मैं रिकॉर्ड को डुप्लिकेट करता हूं, और नए को "अप्रचलित" के रूप में चिह्नित करता हूं ताकि वर्तमान रिकॉर्ड अभी भी समान हो। ध्यान दें कि मैं प्रत्येक तालिका पर एक ट्रिगर बनाना चाहता था लेकिन जब आप इस तालिका के ट्रिगर में होते हैं तो mysql किसी तालिका के संशोधनों को रोक देता है। PostGRESQL ऐसा करता है। SQL सर्वर ऐसा करता है। ओरेकल ऐसा करते हैं। खैर MySQL में अभी भी एक बहुत लंबा रास्ता तय करना है, और अगली बार जब मैं अपने डेटाबेस सर्वर को चुनते समय दो बार सोचूंगा।
ओलिवियर पोंस
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.