COALESCE का उपयोग करके कम्प्यूटेड कॉलम को बनाए रखने के लिए IDENTITY से प्राथमिक कुंजी को बदलना


10

हमारे अखंड डेटाबेस से एक एप्लिकेशन को डिकूप करने के प्रयास में, हमने विभिन्न कंप्यूटर्स वाले INT IDITYITY कॉलम को बदलने के लिए प्रयास किया है जो COALESCE का उपयोग करने वाला PERSISTED कंप्यूटेड कॉलम हो। असल में, हमें कई अनुप्रयोगों में साझा किए गए सामान्य डेटा के लिए डेटाबेस को अपडेट करने के लिए अभी भी डिकोड किए गए एप्लिकेशन की आवश्यकता है, जबकि मौजूदा अनुप्रयोगों को कोड या प्रक्रिया संशोधन की आवश्यकता के बिना इन तालिकाओं में डेटा बनाने की अनुमति है।

इसलिए अनिवार्य रूप से, हम एक स्तंभ की परिभाषा से चले गए हैं;

PkId INT IDENTITY(1,1) PRIMARY KEY

सेवा;

PkId AS AS COALESCE(old_id, external_id, new_id) PERSISTED NOT NULL,
old_id INT NULL, -- Values here are from existing records of PkId before table change
external_id INT NULL,
new_id INT IDENTITY(2000000,1) NOT NULL

सभी मामलों में PkId एक प्राथमिक कुंजी भी है और सभी मामलों में, लेकिन यह स्पष्ट है। सभी तालिकाओं में पहले की तरह ही विदेशी कुंजी और अनुक्रमित हैं। संक्षेप में, नया प्रारूप PkId को डिकोड किए गए एप्लिकेशन (as_id) के रूप में आपूर्ति करने की अनुमति देता है, लेकिन यह PkId को पहचान कॉलम मान भी देता है इसलिए मौजूदा कोड की अनुमति देता है जो SCOPE_IDENTITY और @@ IDENTITY के उपयोग के माध्यम से IDENTITY पर निर्भर करता है। काम करने के लिए के रूप में यह करने के लिए इस्तेमाल किया।

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

यह देखते हुए कि नया कॉलम एक PRIMARY KEY है, पहले जैसा ही डेटा टाइप, और PERSISTED, मुझे उम्मीद थी कि क्वेरी और क्वेरी प्लान उसी तरह का व्यवहार करेंगे जैसा उन्होंने पहले किया था। क्या कम्प्यूटेड INT INT PkId को अनिवार्य रूप से उसी तरह व्यवहार करना चाहिए जैसे SQL सर्वर निष्पादन योजना का उत्पादन करेगा? क्या इस दृष्टिकोण के साथ अन्य संभावित मुद्दे हैं जिन्हें आप देख सकते हैं?

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


टिप्पणियाँ विस्तारित चर्चा के लिए नहीं हैं; इस वार्तालाप को बातचीत में स्थानांतरित कर दिया गया है ।
पॉल व्हाइट 9

जवाबों:


4

प्रथम

: आप शायद सभी तीन स्तंभों की जरूरत नहीं है old_id, external_id, new_idnew_idस्तंभ, एक जा रहा है IDENTITY, प्रत्येक पंक्ति के लिए एक नया मूल्य भी जब आप में सम्मिलित होगा, external_id। लेकिन, बीच old_idऔर external_id, वे बहुत अधिक पारस्परिक रूप से अनन्य हैं: या तो पहले से ही एक old_idमूल्य है या वह कॉलम, वर्तमान गर्भाधान में, बस NULLउपयोग करने पर external_idया होगा new_id। चूँकि आप एक नई "बाहरी" आईडी को एक पंक्ति में नहीं जोड़ेंगे जो पहले से मौजूद है (जिसका old_idमान है), और इसके लिए आने वाले कोई भी नए मान नहीं होंगे old_id, तो इसका उपयोग किया जा सकने वाला एक कॉलम हो सकता है दोनों उद्देश्यों के लिए।

तो, external_idकॉलम से छुटकारा पाएं और old_idकुछ old_or_external_idया कुछ भी होने का नाम बदलें । इसके लिए किसी भी वास्तविक परिवर्तन की आवश्यकता नहीं होनी चाहिए, फिर भी कुछ जटिलताएं कम हो जाती हैं। external_idयदि आपको पहले से ही सम्मिलित करने के लिए एप्लिकेशन कोड लिखा है, तो भी आपको कॉलम को कॉल करने की आवश्यकता हो सकती है , भले ही उसमें "पुराने" मान हों external_id

यह नई संरचना को घटाता है:

PkId AS AS COALESCE(old_or_external_id, new_id, -1) PERSISTED NOT NULL,
old_or_external_id INT NULL, -- values from existing record OR passed in from app
new_id INT IDENTITY(2000000, 1) NOT NULL

अब आपने 12 बाइट्स के बजाय प्रति पंक्ति केवल 8 बाइट्स जोड़े हैं (यह मानते हुए कि आप SPARSEविकल्प या डेटा संपीड़न का उपयोग नहीं कर रहे हैं )। और आपको किसी भी कोड, T-SQL या App कोड को बदलने की आवश्यकता नहीं थी।

दूसरा

सरलीकरण के इस रास्ते को जारी रखते हुए, आइए देखें कि हमने क्या छोड़ा है:

  • old_or_external_idकॉलम को या तो पहले से ही मान होते हैं, या ऐप्लिकेशन से एक नया मूल्य दिया जाएगा, या के रूप में छोड़ दिया जाएगा NULL
  • new_idहमेशा एक नया मूल्य होगा, लेकिन अगर है कि मूल्य केवल उपयोग किया जाएगा old_or_external_idस्तंभ है NULL

वहाँ एक समय था जब तुम दोनों में मूल्यों की आवश्यकता होगी कभी नहीं है old_or_external_idऔर new_id। दोनों स्तंभ के कारण मान हैं जब हाँ, कई बार ऐसा हो जाएगा new_idएक किया जा रहा है IDENTITY, लेकिन उन new_idमूल्यों को नजरअंदाज कर दिया जाता है। फिर, ये दो क्षेत्र परस्पर अनन्य हैं। तो अब क्या?

अब हम इस बात पर गौर कर सकते हैं कि हमें external_idपहली जगह की आवश्यकता क्यों थी । यह ध्यान में रखते हुए कि एक IDENTITYकॉलम का उपयोग करना संभव है SET IDENTITY_INSERT {table_name} ON;, आप किसी भी स्कीमा परिवर्तन को पूरा करने के साथ दूर हो सकते हैं, और केवल INSERTस्टेटमेंट / ऑपरेशंस SET IDENTITY_INSERT {table_name} ON;और SET IDENTITY_INSERT {table_name} OFF;स्टेटमेंट्स को लपेटने के लिए अपने ऐप कोड को संशोधित कर सकते हैं । फिर आपको यह निर्धारित करने की आवश्यकता है कि IDENTITYकॉलम को (नए उत्पन्न मूल्यों के लिए) रीसेट करने के लिए कौन सी शुरुआती सीमा है क्योंकि इसे उन मूल्यों से ऊपर होना चाहिए जो उच्च मूल्य सम्मिलित करने के बाद से ऐप कोड डाले जाएंगे, जिससे अगला ऑटो-जेनरेट किया गया मान होगा वर्तमान अधिकतम मूल्य से अधिक हो। लेकिन आप हमेशा एक मान डाल सकते हैं जो IDENT_CURRENT मान के नीचे है ।

संयोजन old_or_external_idऔर new_idकॉलम भी 2, या यहाँ तक 3 होने के इरादे के बाद से स्वत: जनरेट मूल्यों और एप्लिकेशन-उत्पन्न मूल्यों के बीच एक ओवरलैपिंग मूल्य स्थिति में चल रहा है की संभावना को बढ़ा नहीं है, कॉलम, उन्हें एक प्राथमिक कुंजी मूल्य में गठबंधन करने के लिए है और वे हमेशा अद्वितीय मूल्य हैं।

इस दृष्टिकोण में, आपको बस निम्नलिखित की आवश्यकता है:

  • जा रहा है के रूप में टेबल छोड़ दें:

    PkId INT IDENTITY(1,1) PRIMARY KEY

    यह प्रत्येक पंक्ति में 8 या 12 के बजाय 0 बाइट्स जोड़ता है।

  • एप्लिकेशन-जनरेट किए गए मानों के लिए प्रारंभिक सीमा निर्धारित करें। ये प्रत्येक तालिका में वर्तमान MAX मान से अधिक होंगे, लेकिन स्वतः-जनरेट किए गए मानों के लिए न्यूनतम मान क्या होगा।
  • निर्धारित करें कि ऑटो-जेनरेट की गई रेंज किस मूल्य पर शुरू होनी चाहिए। वर्तमान अधिकतम मूल्य और बढ़ने के लिए बहुत सारे कमरे के बीच में बहुत जगह होनी चाहिए , ऊपरी सीमा पर जानना सिर्फ 2.14 बिलियन से अधिक है। फिर आप इस नए न्यूनतम बीज मान को DBCC CHECKIDENT के माध्यम से सेट कर सकते हैं ।
  • एप्लिकेशन कोड INSERTs SET IDENTITY_INSERT {table_name} ON;और SET IDENTITY_INSERT {table_name} OFF;विवरणों में लपेटें ।

सेकंड, पार्ट बी

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

इस दृष्टिकोण में, आपको बस निम्नलिखित की आवश्यकता है:

  • जा रहा है के रूप में टेबल छोड़ दें:

    PkId INT IDENTITY(1,1) PRIMARY KEY

    यह प्रत्येक पंक्ति में 8 या 12 के बजाय 0 बाइट्स जोड़ता है।

  • ऐप-जनरेट वैल्यू के लिए शुरुआती रेंज होगी -1
  • एप्लिकेशन कोड INSERTs SET IDENTITY_INSERT {table_name} ON;और SET IDENTITY_INSERT {table_name} OFF;विवरणों में लपेटें ।

यहां आपको अभी भी करने की आवश्यकता है IDENTITY_INSERT, लेकिन: आप किसी भी नए कॉलम को नहीं जोड़ते हैं, किसी भी IDENTITYकॉलम को "रीसेट" करने की आवश्यकता नहीं है , और ओवरलैप्स का कोई भविष्य का जोखिम नहीं है।

सेकंड, भाग 3

इस दृष्टिकोण का एक अंतिम बदलाव संभवतः IDENTITYस्तंभों को स्वैप करना होगा और इसके बजाय अनुक्रमों का उपयोग करना होगा । इस दृष्टिकोण को लेने का कारण ऐप कोड डालने के मूल्यों को सक्षम करने में सक्षम होना चाहिए: ऑटो-जेनरेट किए गए रेंज के ऊपर सकारात्मक (नीचे नहीं), और इसकी कोई आवश्यकता नहीं है SET IDENTITY_INSERT ON / OFF

इस दृष्टिकोण में, आपको बस निम्नलिखित की आवश्यकता है:

  • क्रीक दृश्य का उपयोग कर अनुक्रम बनाएँ
  • IDENTITYस्तंभ को उस नए कॉलम में कॉपी करें जिसमें IDENTITYसंपत्ति नहीं है , लेकिन फ़ंक्शन के लिए NEXT VALUEDEFAULT का उपयोग करके एक बाधा है :

    PkId INT PRIMARY KEY CONSTRAINT [DF_TableName_NextID] DEFAULT (NEXT VALUE FOR...)

    यह प्रत्येक पंक्ति में 8 या 12 के बजाय 0 बाइट्स जोड़ता है।

  • एप्लिकेशन-जनरेट किए गए मानों के लिए आरंभिक सीमा उस से ऊपर होगी जो आपको लगता है कि स्वतः-जनरेट किए गए मान एप्रोच करेंगे।
  • एप्लिकेशन कोड INSERTs SET IDENTITY_INSERT {table_name} ON;और SET IDENTITY_INSERT {table_name} OFF;विवरणों में लपेटें ।

फिर भी , आवश्यकता के कारण उस कोड के साथ या SCOPE_IDENTITY()फिर @@IDENTITYभी ठीक से काम करता है, अनुक्रम पर स्विच करना वर्तमान में एक विकल्प नहीं है क्योंकि ऐसा प्रतीत होता है कि अनुक्रम के लिए उन कार्यों के बराबर नहीं है :-( दुखद!


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

@MrMoose हाँ, मैंने अंत में अनुक्रमों के बारे में अधिक जानकारी शामिल करने के लिए अपना जवाब अपडेट किया। यह वैसे भी आपकी स्थिति में काम नहीं करेगा। और मैं संभावित समसामयिक मुद्दों के बारे में सोच रहा था IDENTITY_INSERT, लेकिन इसका परीक्षण नहीं किया। सुनिश्चित नहीं है कि विकल्प # 1 आपके समग्र मुद्दे को हल करने वाला है, यह केवल अनावश्यक जटिलता को कम करने के लिए एक अवलोकन था। फिर भी, यदि आपके पास नए "बाहरी" आईडी डालने वाले कई धागे हैं, तो आप कैसे गारंटी देते हैं कि वे अद्वितीय हैं?
सुलैमान रुट्ज़की

@MrMoose दरअसल, " IDENTITY_INSERT का उपयोग केवल प्रति सत्र एक टेबल के लिए किया जा सकता है ", यहाँ वास्तव में क्या समस्या है? 1) आप एक समय में केवल एक तालिका में सम्मिलित कर सकते हैं, इसलिए आप इसे TableB में डालने से पहले TableA के लिए बंद कर देते हैं, और 2) मैंने अभी-अभी परीक्षण किया है और जो मैंने सोचा था, उसके विपरीत, कोई निर्णायक मुद्दे नहीं हैं - मैं सक्षम था राशि IDENTITY_INSERT ONदो सत्रों में एक ही तालिका के लिए और कोई समस्या नहीं के साथ दोनों में डालने गया था।
सुलैमान रटज़की

1
जैसा कि आपने सुझाव दिया, परिवर्तन 1 ने थोड़ा अंतर किया। हमारे द्वारा उपयोग की जाने वाली आईडी को वर्तमान डेटाबेस से बाहर आवंटित किया जाएगा और रिकॉर्ड से संबंधित करने के लिए उपयोग किया जाएगा। यह अच्छी तरह से हो सकता है कि सत्रों के बारे में मेरी समझ काफी सही नहीं है, इसलिए IDENTITY_INSERT काम कर सकता है। हालांकि मुझे इसकी जांच करने में थोड़ा समय लगेगा, इसलिए मैं थोड़ी देर के लिए वापस रिपोर्ट नहीं कर पाऊंगा। इनपुट के लिए पुनः धन्यवाद। अत्यन्त सराहनीय।
मि।

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