सशर्त अद्वितीय बाधा


93

मेरे पास एक ऐसी स्थिति है जहां मुझे स्तंभों के सेट पर एक अद्वितीय बाधा को लागू करने की आवश्यकता है, लेकिन केवल एक स्तंभ के एक मूल्य के लिए।

इसलिए उदाहरण के लिए मेरे पास टेबल (आईडी, नाम, रिकॉर्डस्टैटस) जैसी तालिका है।

RecordStatus का केवल एक मान 1 या 2 (सक्रिय या हटाया गया) हो सकता है, और मैं केवल एक अद्वितीय अवरोध बनाना चाहता हूं (ID, RecordStatus) केवल जब RecordStatus = 1, तो मुझे परवाह नहीं है अगर एक ही साथ कई हटाए गए रिकॉर्ड नहीं हैं आईडी।

ट्रिगर्स लिखने के अलावा, क्या मैं ऐसा कर सकता हूं?

मैं SQL Server 2005 का उपयोग कर रहा हूं।


1
यह डिजाइन एक आम दर्द है। क्या आपने डिज़ाइन को बदलने पर विचार किया है ताकि शारीरिक रूप से 'हटाए गए' रिकॉर्ड्स को तालिका से हटा दिया जाए और संभवत: 'संग्रह' तालिका में स्थानांतरित कर दिया जाए?
onedaywhen

1
... क्योंकि एक साधारण कुंजी को लागू करने के लिए एक UNIQUE बाधा लिखने में असमर्थता को 'कोड गंध', IMO माना जाना चाहिए। यदि आप डिज़ाइन (SQL DDL) को बदल नहीं सकते क्योंकि कई अन्य तालिकाएँ इस तालिका को संदर्भित करती हैं तो मैं दांव लगाऊंगा कि आपका SQL DML भी परिणामित होता है अर्थात आपको इसे जोड़ना होगा ... और Table.RecordStatus = 1 ' अधिकांश खोज स्थितियों में और इस तालिका में शामिल होने वाली परिस्थितियों में शामिल होने और सूक्ष्म बग का अनुभव करने पर जब यह अनिवार्य रूप से अवसर पर छोड़ दिया जाता है।
प्रात:

जवाबों:


36

इस तरह एक चेक बाधा जोड़ें। अंतर यह है कि, यदि स्थिति = 1 और गिनती> 0 है तो आप झूठे लौटा देंगे।

http://msdn.microsoft.com/en-us/library/ms188258.aspx

CREATE TABLE CheckConstraint
(
  Id TINYINT,
  Name VARCHAR(50),
  RecordStatus TINYINT
)
GO

CREATE FUNCTION CheckActiveCount(
  @Id INT
) RETURNS INT AS BEGIN

  DECLARE @ret INT;
  SELECT @ret = COUNT(*) FROM CheckConstraint WHERE Id = @Id AND RecordStatus = 1;
  RETURN @ret;

END;
GO

ALTER TABLE CheckConstraint
  ADD CONSTRAINT CheckActiveCountConstraint CHECK (NOT (dbo.CheckActiveCount(Id) > 1 AND RecordStatus = 1));

INSERT INTO CheckConstraint VALUES (1, 'No Problems', 2);
INSERT INTO CheckConstraint VALUES (1, 'No Problems', 2);
INSERT INTO CheckConstraint VALUES (1, 'No Problems', 2);
INSERT INTO CheckConstraint VALUES (1, 'No Problems', 1);

INSERT INTO CheckConstraint VALUES (2, 'Oh no!', 1);
INSERT INTO CheckConstraint VALUES (2, 'Oh no!', 2);
-- Msg 547, Level 16, State 0, Line 14
-- The INSERT statement conflicted with the CHECK constraint "CheckActiveCountConstraint". The conflict occurred in database "TestSchema", table "dbo.CheckConstraint".
INSERT INTO CheckConstraint VALUES (2, 'Oh no!', 1);

SELECT * FROM CheckConstraint;
-- Id   Name         RecordStatus
-- ---- ------------ ------------
-- 1    No Problems  2
-- 1    No Problems  2
-- 1    No Problems  2
-- 1    No Problems  1
-- 2    Oh no!       1
-- 2    Oh no!       2

ALTER TABLE CheckConstraint
  DROP CONSTRAINT CheckActiveCountConstraint;

DROP FUNCTION CheckActiveCount;
DROP TABLE CheckConstraint;

मैंने टेबल लेवल चेक की कमी को देखा, लेकिन फ़ंक्शन में डाले गए या अपडेट किए गए मानों को पास करने का कोई तरीका नहीं है, क्या आप जानते हैं कि कैसे करना है?
एनपी-हार्ड

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

मैं एक ट्रिगर में एक ही तर्क पसंद करूँगा। "एक स्केलर फ़ंक्शन में एक क्वेरी ... बड़ी समस्याएं पैदा कर सकती है यदि आपका CHECK बाधा किसी क्वेरी पर निर्भर करता है और यदि किसी भी अद्यतन से एक से अधिक पंक्ति प्रभावित होती है। क्या होता है कि कथन पूरा होने से पहले ही प्रत्येक पंक्ति के लिए एक बार जाँच हो जाती है। । इसका मतलब है कि बयान की असमानता टूट गई है और फ़ंक्शन असंगत स्थिति में डेटाबेस के संपर्क में आ जाएगा। परिणाम अप्रत्याशित और गलत हैं। " देखें: blogs.conchango.com/davidportas/archive/2007/02/19/…
onedaywhen

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

3
यह आवेषण के लिए बहुत अच्छा काम करता है लेकिन अपडेट के लिए काम नहीं करता है। ईजी जोड़ना अन्य आवेषण के बाद यह काम करता है जब मुझे इसकी उम्मीद नहीं थी। INSERT INTO CheckConstraint VALUES (1, 'कोई समस्या नहीं', 2); CheckConstraint set Recordstatus = 1 जहां नाम = 'कोई समस्या नहीं है'
अपडेट करें

149

निहारना, फ़िल्टर किया गया सूचकांक । प्रलेखन से (जोर मेरा):

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

और यहाँ एक उदाहरण एक फिल्टर के साथ एक अद्वितीय सूचकांक के संयोजन की भविष्यवाणी करता है:

create unique index MyIndex
on MyTable(ID)
where RecordStatus = 1;

यह अनिवार्य रूप से IDजब RecordStatusहै की विशिष्टता को लागू करता है 1

उस सूचकांक के निर्माण के बाद, एक विशिष्टता उल्लंघन एक बन्दी को उठाएगा:

Msg 2601, Level 14, State 1, Line 13
अद्वितीय इंडेक्स 'MyIndex' के साथ ऑब्जेक्ट 'dbo.MyTable' में डुप्लिकेट कुंजी पंक्ति नहीं डाल सकता है। डुप्लिकेट कुंजी मान (9999) है।

नोट: फ़िल्टर्ड इंडेक्स SQL ​​Server 2008 में पेश किया गया था। SQL सर्वर के पुराने संस्करणों के लिए, कृपया यह उत्तर देखें ।


ध्यान दें कि SQL सर्वर को ansi_paddingफ़िल्टर्ड इंडेक्स के लिए आवश्यक है , इसलिए सुनिश्चित करें कि SET ANSI_PADDING ONफ़िल्टर्ड इंडेक्स बनाने से पहले निष्पादित करके इस विकल्प को चालू किया जाए ।
12

10

आप हटाए गए रिकॉर्डों को एक मेज पर ले जा सकते हैं जिसमें कमी की कमी है, और शायद एकल तालिका की उपस्थिति को संरक्षित करने के लिए दो तालिकाओं के यूनिअन के साथ एक दृश्य का उपयोग करें।


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

3

आप वास्तव में हैकी तरीके से ऐसा कर सकते हैं ...

अपनी मेज पर एक स्कीमाबाउंड दृश्य बनाएं।

तालिका से कहीं भी जो कुछ भी * प्राप्त होता है उसे रिकॉर्ड करें = 1

अब अपने इच्छित फ़ील्ड के साथ दृश्य पर एक अद्वितीय बाधा बनाएँ।

स्कीमाबाउंड दृश्यों के बारे में एक नोट हालांकि, यदि आप अंतर्निहित तालिकाओं को बदलते हैं तो आपको दृश्य को फिर से बनाना होगा। उस वजह से बहुत सारे गोचर्स।


यह एक बहुत अच्छा सुझाव है, न कि "हैकी"। इस फ़िल्टर्ड इंडेक्स विकल्प के बारे में अधिक जानकारी यहाँ दी गई है ।
स्कॉट व्हिटलॉक

यह एक बुरा विचार है। सवाल यह नहीं है।
FabianoLothor

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

1

क्योंकि, आप डुप्लिकेट की अनुमति देने जा रहे हैं, एक अद्वितीय बाधा काम नहीं करेगी। आप डुप्लिकेट आईडी डालने से पहले मौजूदा सक्रिय रिकॉर्ड की जांच करने वाले INSERT के लिए RecordStatus कॉलम के लिए एक चेक बाधा और एक संग्रहीत प्रक्रिया बना सकते हैं।


1

यदि आप बिल के सुझाव के अनुसार NULL को रिकॉर्डस्टैटस के रूप में उपयोग नहीं कर सकते हैं, तो आप उनके विचार को फ़ंक्शन-आधारित इंडेक्स के साथ जोड़ सकते हैं। एक फ़ंक्शन बनाएं जो NULL को लौटाता है यदि RecordStatus आपके मानों (और RecordStatus अन्यथा) पर विचार करने वाले मूल्यों में से एक नहीं है और उस पर एक इंडेक्स बनाएं।

इससे आपको यह फायदा होगा कि आपको अपने अवरोध में तालिका की अन्य पंक्तियों की स्पष्ट रूप से जांच करने की आवश्यकता नहीं है, जिससे आपको प्रदर्शन संबंधी समस्याएं हो सकती हैं।

मुझे कहना चाहिए कि मैं SQL सर्वर को बिल्कुल नहीं जानता, लेकिन मैंने Oracle में इस दृष्टिकोण का सफलतापूर्वक उपयोग किया है।


अच्छा विचार है, लेकिन जवाब के लिए एसक्यूएल सर्वर धन्यवाद में कोई फ़ंक्शन आधारित अनुक्रमित नहीं हैं
np-hard
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.