PostgreSQL CHECK कंस्ट्रेंट: पंक्ति-स्थानीय नियम और क्रॉस-रो तुलना डंप क्यों तोड़ती है
PostgreSQL CHECK कंस्ट्रेंट केवल उसी पंक्ति को संदर्भित कर सकता है जिसे सम्मिलित या अद्यतन किया जा रहा है। अन्य पंक्तियों से तुलना करने वाला CHECK सरल परीक्षणों में पास हो सकता है, लेकिन pg_dump पुनर्स्थापन के दौरान विफल हो सकता है, क्योंकि पंक्तियाँ ऐसे क्रम में लोड होती हैं जो उसे संतुष्ट न करे।
इस पृष्ठ पर
मुख्य विचार
PostgreSQL में, CHECK कंस्ट्रेंट एक बूलियन अभिव्यक्ति है जिसका मूल्यांकन केवल नई या अद्यतन की गई पंक्ति के विरुद्ध किया जाता है। यह उस पंक्ति के स्तंभों, स्थिरांकों, अपरिवर्तनीय (immutable) फ़ंक्शनों और ऑपरेटरों को संदर्भित कर सकता है, लेकिन इसे अन्य पंक्तियों या अन्य तालिकाओं को संदर्भित नहीं करना चाहिए। PostgreSQL परिभाषा के समय इस प्रतिबंध को लागू नहीं करता, इसलिए क्रॉस-रो CHECK बनाया जा सकता है और छोटे परीक्षणों में काम करता प्रतीत हो सकता है। हालाँकि, यह अपरिवर्तनीयता (invariant) की गारंटी नहीं दे सकता, क्योंकि संदर्भित पंक्ति में बाद के परिवर्तन मूल पंक्ति की पुनः जाँच किए बिना शर्त को गलत बना सकते हैं। प्रलेखित परिणाम यह है कि डेटाबेस डंप और पुनर्स्थापन विफल हो सकता है: पंक्तियाँ ऐसे क्रम में पुनः लोड होती हैं जो कंस्ट्रेंट को संतुष्ट न करे, भले ही अंतिम डेटाबेस स्थिति सुसंगत हो। क्रॉस-रो या क्रॉस-टेबल नियमों के लिए, UNIQUE, EXCLUDE, या FOREIGN KEY कंस्ट्रेंट का उपयोग करें, या नियम को एप्लिकेशन लॉजिक या ट्रिगर में लागू करें। यह भी याद रखें कि जब अभिव्यक्ति NULL का मूल्यांकन करती है तो CHECK पास हो जाता है, इसलिए जब nulls को बाहर रखना हो तो इसे NOT NULL के साथ जोड़ें।
अनुमेय CHECK कंस्ट्रेंट
PostgreSQL में CHECK कंस्ट्रेंट सबसे सामान्य कंस्ट्रेंट प्रकार है। यह किसी स्तंभ या तालिका से एक बूलियन अभिव्यक्ति संलग्न करता है, और जब भी कोई पंक्ति सम्मिलित या अद्यतन की जाती है तो अभिव्यक्ति का मूल्यांकन किया जाता है। अभिव्यक्ति में प्रतिबंधित स्तंभ शामिल होना चाहिए, अन्यथा यह बहुत कम उपयोगी होती है। स्तंभ कंस्ट्रेंट और तालिका कंस्ट्रेंट कई मामलों में परस्पर विनिमेय हैं, और एक तालिका कंस्ट्रेंट एक ही पंक्ति के कई स्तंभों को संदर्भित कर सकता है।
अभिव्यक्ति जाँची जा रही पंक्ति के स्तंभों, शाब्दिक स्थिरांकों, ऑपरेटरों और फ़ंक्शनों का उपयोग कर सकती है। इसे नई या अद्यतन की गई पंक्ति के अलावा किसी अन्य तालिका डेटा को संदर्भित नहीं करना चाहिए। यह एक प्रलेखित प्रतिबंध है, कोई शैलीगत प्राथमिकता नहीं। कंस्ट्रेंट का मूल्यांकन उम्मीदवार पंक्ति के विरुद्ध अलग-अलग किया जाता है, इसलिए मूल्यांकन के समय इसकी अन्य पंक्तियों तक कोई पहुँच नहीं होती।
एक सूक्ष्मता जो कई व्यवसायियों को आश्चर्यचकित करती है, वह है null हैंडलिंग। CHECK कंस्ट्रेंट तब संतुष्ट होता है जब अभिव्यक्ति true या null मान का मूल्यांकन करती है। चूँकि अधिकांश अभिव्यक्तियाँ तब null देती हैं जब कोई भी ऑपरेंड null हो, एक CHECK अपने आप प्रतिबंधित स्तंभों में null मानों को नहीं रोकता। nulls को मना करने के लिए, NOT NULL कंस्ट्रेंट जोड़ें, जो कार्यात्मक रूप से CHECK (column IS NOT NULL) के समतुल्य है लेकिन PostgreSQL में अधिक कुशल है।
CREATE TABLE products (
product_no integer,
name text,
price numeric CHECK (price > 0),
discounted_price numeric,
CHECK (price > discounted_price)
);क्रॉस-रो संदर्भों का जोखिम
PostgreSQL उन CHECK कंस्ट्रेंट का समर्थन नहीं करता जो जाँची जा रही नई या अद्यतन की गई पंक्ति के अलावा तालिका डेटा को संदर्भित करते हैं। दस्तावेज़ीकरण स्पष्ट है कि इस नियम का उल्लंघन करने वाला CHECK सरल परीक्षणों में काम करता प्रतीत हो सकता है, लेकिन यह गारंटी नहीं दे सकता कि डेटाबेस ऐसी स्थिति में नहीं पहुँचेगा जिसमें कंस्ट्रेंट शर्त गलत हो। कारण यह है कि शर्त अन्य पंक्तियों पर निर्भर करती है, और वे पंक्तियाँ मूल पंक्ति के सत्यापन के बाद बदल सकती हैं। जब संदर्भित पंक्ति संशोधित होती है तो मूल पंक्ति की पुनः जाँच नहीं होती।
यह एक शुद्धता की समस्या है, केवल प्रदर्शन की नहीं। एक कंस्ट्रेंट जो केवल सम्मिलन के समय लागू होता है, अखंडता का झूठा आभास देता है। डेटाबेस ऐसी स्थिति में बहक सकता है जहाँ अपरिवर्तनीयता का उल्लंघन होता है, और बहकाव के क्षण में कोई त्रुटि नहीं उठाई जाती। विफलता बाद में सामने आती है, अक्सर सबसे बुरे संभव समय पर।
यही तर्क CHECK के भीतर उपयोग किए जाने वाले फ़ंक्शनों पर भी लागू होता है। अन्य तालिकाओं को पढ़ने वाला फ़ंक्शन वही क्रॉस-रो निर्भरता पेश करता है, भले ही सिंटैक्स स्थानीय दिखता हो। प्रतिबंध इस बारे में है कि अभिव्यक्ति क्या देख सकती है, इस बारे में नहीं कि यह कैसे लिखी गई है।
डंप और रिस्टोर विफलताएँ
क्रॉस-रो CHECK का प्रलेखित परिणाम यह है कि डेटाबेस डंप और रिस्टोर विफल हो सकता है। रिस्टोर के दौरान, पंक्तियाँ डंप द्वारा निर्धारित क्रम में लोड की जाती हैं, और वह क्रम प्रत्येक मध्यवर्ती चरण में कंस्ट्रेंट को संतुष्ट नहीं कर सकता। रिस्टोर तब भी विफल हो सकता है जब संपूर्ण डेटाबेस स्थिति कंस्ट्रेंट के अनुरूप हो, क्योंकि डेटा सम्मिलित होते समय कंस्ट्रेंट की पंक्ति-दर-पंक्ति मूल्यांकन की जाती है।
यह समस्या को परिचालन की दृष्टि से गंभीर बनाता है। विकास में सफलतापूर्वक रिस्टोर होने वाला बैकअप उत्पादन में विफल हो सकता है यदि पंक्ति क्रम भिन्न हो, यदि डेटा की मात्रा लोड क्रम को बदल दे, या यदि डंप डेटा जीवनचक्र के किसी भिन्न बिंदु पर लिया गया हो। विफलता अंतिम स्थिति के संबंध में नियतात्मक नहीं है; यह उस स्थिति तक पहुँचने के मार्ग पर निर्भर करती है।
व्यावहारिक सीख यह है कि जिस कंस्ट्रेंट का मूल्यांकन एक ही पंक्ति से नहीं किया जा सकता, उसे घोषणात्मक गारंटी के रूप में भरोसा नहीं किया जा सकता। यह परीक्षण पास कर सकता है, और यह किसी एक अवसर पर रिस्टोर भी पास कर सकता है, लेकिन यह वह अखंडता गुण प्रदान नहीं करता जो यह प्रतीत होता है कि प्रदान करता है।
अनुशंसित विकल्प
जब कोई नियम वास्तव में पंक्तियों या तालिकाओं तक फैला हो, तो PostgreSQL उस उद्देश्य के लिए डिज़ाइन किए गए घोषणात्मक कंस्ट्रेंट प्रदान करता है। पंक्तियों में विशिष्टता के लिए UNIQUE का उपयोग करें, रेंज और ओवरलैप नियमों के लिए EXCLUDE का, और संदर्भात्मक अखंडता के लिए FOREIGN KEY का। इन कंस्ट्रेंट को डेटाबेस द्वारा संबंधित पंक्तियों के विरुद्ध लागू किया जाता है और डेटा बदलने पर सही ढंग से बनाए रखा जाता है।
जिन नियमों को इनमें से कोई भी व्यक्त नहीं करता, उनके लिए ट्रिगर या अनुप्रयोग-स्तरीय सत्यापन का उपयोग करें, और स्पष्ट रूप से दस्तावेज़ित करें कि नियम घोषणात्मक कंस्ट्रेंट नहीं है। एक ट्रिगर अन्य पंक्तियों का निरीक्षण कर सकता है और संबंधित परिवर्तनों पर पुनः सत्यापन के लिए लिखा जा सकता है, लेकिन इसमें अपनी जटिलता होती है और इसे सावधानीपूर्वक बनाए रखा जाना चाहिए।
एक उपयोगी मानसिक मॉडल यह पूछना है कि क्या नियम अकेले उम्मीदवार पंक्ति से तय किया जा सकता है। यदि हाँ, तो CHECK उपयुक्त और सस्ता है। यदि नहीं, तो नियम उस कंस्ट्रेंट प्रकार का है जो संबंध को समझता है, या उस प्रक्रियात्मक कोड का है जिसे आप प्रवर्तन बिंदु के रूप में स्वीकार करते हैं।
CREATE TABLE products (
product_no integer PRIMARY KEY,
name text NOT NULL,
price numeric NOT NULL CHECK (price > 0),
discounted_price numeric CHECK (discounted_price > 0),
CONSTRAINT valid_discount CHECK (price > discounted_price)
);उपयोग की शर्तें
- क्या CHECK अभिव्यक्ति केवल उसी पंक्ति के स्तंभों को संदर्भित करती है जिसे सम्मिलित या अद्यतन किया जा रहा है?
- क्या नियम में अन्य पंक्तियाँ या अन्य तालिकाएँ शामिल हैं, जिनके लिए UNIQUE, EXCLUDE, या FOREIGN KEY की आवश्यकता होगी?
- क्या nullable स्तंभों को NOT NULL के साथ जोड़ा गया है जहाँ nulls को बाहर रखना आवश्यक है?
- क्या आपने तालिका का डंप और पुनर्स्थापन परीक्षण किया है ताकि पुष्टि हो सके कि कंस्ट्रेंट पुनः लोडिंग में बना रहता है?
- क्या कंस्ट्रेंट का नाम इस प्रकार रखा गया है कि बाद में इसे पहचाना और बदला जा सके?
उपयोग की सीमाएँ
यह लेख समर्थित संस्करणों (लेखन के समय 14 से 18) के लिए प्रलेखित PostgreSQL व्यवहार का वर्णन करता है। त्रुटि संदेशों की सटीक शब्दावली और डंप तथा पुनर्स्थापन का व्यवहार संस्करण और उपयोग किए गए उपकरणों (pg_dump, pg_restore, या लॉजिकल रेप्लिकेशन) के अनुसार भिन्न हो सकता है। उदाहरण केवल दृष्टांत हैं और कस्टम कंस्ट्रेंट ट्रिगर के बिना डिफ़ॉल्ट इंस्टॉलेशन मानते हैं। लेख डिफर्ड कंस्ट्रेंट, एक्सक्लूज़न कंस्ट्रेंट ऑपरेटरों, या ट्रिगर-आधारित प्रवर्तन को विस्तार से कवर नहीं करता; उनके लिए अलग उपचार आवश्यक है। यह यह भी दावा नहीं करता कि कोई विशेष पुनर्स्थापन विफल होगा, केवल यह कि क्रॉस-रो CHECK अखंडता की गारंटी नहीं दे सकता और पंक्ति लोड क्रम के आधार पर पुनर्स्थापन को विफल कर सकता है।