क्या मैं एक अद्वितीय बाधा जोड़ सकता हूं जो मौजूदा उल्लंघनों की अनदेखी करता है?


40

मेरे पास एक तालिका है जिसमें वर्तमान में एक कॉलम में डुप्लिकेट मान हैं।

मैं इन गलत डुप्लिकेट को हटा नहीं सकता, लेकिन मैं अतिरिक्त गैर-अद्वितीय मानों को जोड़े जाने से रोकना चाहूंगा।

क्या मैं ऐसा बना सकता हूँ UNIQUEजो मौजूदा अनुपालन के लिए जाँच नहीं करता है?

मैंने प्रयोग करने की कोशिश की है NOCHECKलेकिन असफल रहा।

इस मामले में मेरे पास एक तालिका है जो "CompanyName" को लाइसेंसिंग जानकारी देती है

संपादित करें: एक ही "CompanyName" के साथ कई पंक्तियाँ होना बुरा डेटा है, लेकिन हम इस समय उन डुप्लिकेट को निकाल या अपडेट नहीं कर सकते हैं। एक दृष्टिकोण के लिए INSERTएक संग्रहीत प्रक्रिया का उपयोग करना है जो डुप्लिकेट के लिए विफल हो जाएगा ... यदि यह संभव था कि SQL अपने आप में अद्वितीयता की जांच करे, तो यह बेहतर होगा।

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


क्या आपको तालिका बदलने (एक और कॉलम जोड़ने) की अनुमति है?
ypercube y

@ypercube दुर्भाग्य से नहीं।
मैथ्यू

जवाबों:


33

इसका जवाब है हाँ"। आप इसे फ़िल्टर किए गए इंडेक्स ( प्रलेखन के लिए यहां देखें ) के साथ कर सकते हैं ।

उदाहरण के लिए, आप कर सकते हैं:

create unique index t_col on t(col) where id > 1000;

यह एक अद्वितीय सूचकांक बनाता है, केवल नई पंक्तियों पर, बल्कि पुरानी पंक्तियों पर। यह विशेष सूत्रीकरण मौजूदा मूल्यों के साथ डुप्लिकेट की अनुमति देगा।

यदि आपके पास केवल कुछ डुप्लिकेट हैं, तो आप कुछ ऐसा कर सकते हैं:

create unique index t_col on t(col) where id not in (<list of ids for duplicate values here>);

2
भले ही वह अच्छा हो या नहीं, यह इस बात पर निर्भर करेगा कि "पुरानी" मौजूदा वस्तुओं को उसी मूल्य के साथ नई वस्तुओं के निर्माण को रोकना चाहिए या नहीं।
सुपर

1
@ सपरकट। । । मैंने मौजूदा डुप्लिकेट मानों को छोड़कर सभी चीजों पर सूचकांक बनाने के लिए एक वैकल्पिक सूत्रीकरण दिया।
गॉर्डन लिनोफ़

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

@ सपरकट। । । मैं सहमत हूँ। अपडेट और डिलीट के लिए इंडेक्स को लगातार बनाए रखना सभी अधिक चुनौतीपूर्ण है क्योंकि आप एक ट्रिगर में इंडेक्स को फिर से नहीं बना सकते हैं। किसी भी मामले में, मुझे ओपी से यह धारणा थी कि डेटा - या कम से कम डुप्लिकेट - अक्सर बदल नहीं रहे हैं, अगर बिल्कुल।
गॉर्डन लिनोफ

आईडी की सूची के बजाय मानों की सूची को बाहर क्यों नहीं किया जाता है? फिर आपको बाहर की गई आईडी की सूची से एक आईडी प्रति डुप्लिकेट मान को बाहर करने की आवश्यकता नहीं है
जेएमडी कोलेसस

23

हां, आप यह कर सकते हैं।

यहाँ डुप्लिकेट के साथ एक तालिका है:

CREATE TABLE dbo.Party
  (
    ID INT NOT NULL
           IDENTITY ,
    CONSTRAINT PK_Party PRIMARY KEY ( ID ) ,
    Name VARCHAR(30) NOT NULL
  ) ;
GO

INSERT  INTO dbo.Party
        ( Name )
VALUES  ( 'Frodo Baggins' ),
        ( 'Luke Skywalker' ),
        ( 'Luke Skywalker' ),
        ( 'Harry Potter' ) ;
GO

आइए हम मौजूदा को अनदेखा करें, और सुनिश्चित करें कि कोई नया डुप्लिकेट नहीं जोड़ा जा सकता है:

-- Add a new column to mark grandfathered duplicates.
ALTER TABLE dbo.Party ADD IgnoreThisDuplicate INT NULL ;
GO

-- The *first* instance will be left NULL.
-- *Secondary* instances will be set to their ID (a unique value).
UPDATE  dbo.Party
SET     IgnoreThisDuplicate = ID
FROM    dbo.Party AS my
WHERE   EXISTS ( SELECT *
                 FROM   dbo.Party AS other
                 WHERE  other.Name = my.Name
                        AND other.ID < my.ID ) ;
GO

-- This constraint is not strictly necessary.
-- It prevents granting further exemptions beyond the ones we made above.
ALTER TABLE dbo.Party WITH NOCHECK
ADD CONSTRAINT CHK_Party_NoNewExemptions 
CHECK(IgnoreThisDuplicate IS NULL);
GO

SELECT * FROM dbo.Party;
GO

-- **THIS** is our pseudo-unique constraint.
-- It works because the grandfathered duplicates have a unique value (== their ID).
-- Non-grandfathered records just have NULL, which is not unique.
CREATE UNIQUE INDEX UNQ_Party_UniqueNewNames ON dbo.Party(Name, IgnoreThisDuplicate);
GO

आइए हम इस समाधान का परीक्षण करें:

-- cannot add a name that exists
INSERT  INTO dbo.Party
        ( Name )
VALUES  ( 'Frodo Baggins' );

Cannot insert duplicate key row in object 'dbo.Party' with unique index 'UNQ_Party_UniqueNewNames'.

-- cannot add a name that exists and has an ignored duplicate
INSERT  INTO dbo.Party
        ( Name )
VALUES  ( 'Luke Skywalker' );

Cannot insert duplicate key row in object 'dbo.Party' with unique index 'UNQ_Party_UniqueNewNames'.


-- can add a new name 
INSERT  INTO dbo.Party
        ( Name )
VALUES  ( 'Hamlet' );

-- but only once
INSERT  INTO dbo.Party
        ( Name )
VALUES  ( 'Hamlet' );

Cannot insert duplicate key row in object 'dbo.Party' with unique index 'UNQ_Party_UniqueNewNames'.

4
सिवाय वह तालिका में एक कॉलम नहीं जोड़ सकता।
हारून बर्ट्रेंड

3
मुझे पसंद है कि यह उत्तर कैसे बदल जाता है कि कैसे NULL मानों को गैर-मानक तरीके से कुछ उपयोगी में अद्वितीय बाधा के रूप में व्यवहार किया जाता है। चालाक चाल।
ypercube y

@ ypercube y, क्या आप बता सकते हैं कि अद्वितीय बाधाओं में NULL हैंडलिंग के बारे में गैर-मानक क्या है? यह किस तरह से अलग है जो आपने उम्मीद की होगी? धन्यवाद!
नोआच

1
@ SQL सर्वर में, एक अशक्त UNIQUEस्तंभ में एक बाधा यह सुनिश्चित करती है कि अधिकांश एकल NULLमान है। SQL मानक (और लगभग सभी अन्य SQL DBMS) का कहना है कि इसे किसी भी संख्या में NULLमानों की अनुमति देनी चाहिए (यानी बाधा को शून्य मानों को अनदेखा करना चाहिए)।
ypercube y

@ ypercube y तो एक अलग DBMS पर इसे लागू करने के लिए, हमें केवल NULL के बजाय DEFAULT 0 का उपयोग करने की आवश्यकता है। सही बात?
Noach

16

फ़िल्टर्ड यूनिक इंडेक्स एक शानदार विचार है, लेकिन इसका एक मामूली नुकसान है - कोई फर्क नहीं पड़ता कि आप WHERE identity_column > <current value>स्थिति का उपयोग करते हैं या WHERE identity_column NOT IN (<list of ids for duplicate values here>)

पहले दृष्टिकोण के साथ, आप अभी भी भविष्य में डुप्लिकेट डेटा, मौजूदा (अब) डेटा के डुप्लिकेट को सम्मिलित करने में सक्षम होंगे। उदाहरण के लिए, यदि आपके पास (यहां तक ​​कि केवल एक) पंक्ति है CompanyName = 'Software Inc.', तो सूचकांक आपके कंपनी के नाम के साथ एक और पंक्ति के सम्मिलन को मना नहीं करेगा। यदि आप दो बार कोशिश करते हैं तो यह केवल इसे मना करेगा।

दूसरे दृष्टिकोण के साथ एक सुधार है, ऊपर काम नहीं करेगा (जो अच्छा है।) हालांकि, आप अभी भी अधिक डुप्लिकेट या मौजूदा डुप्लिकेट सम्मिलित कर पाएंगे। उदाहरण के लिए, यदि आपके पास अब (दो या अधिक) पंक्तियाँ हैं CompanyName = 'DoubleData Co.', तो सूचकांक आपके कंपनी नाम के साथ एक और पंक्ति के सम्मिलन की मनाही नहीं करेगा। यदि आप दो बार कोशिश करते हैं तो यह केवल इसे मना करेगा।

(अपडेट) इसे सही किया जा सकता है यदि प्रत्येक डुप्लिकेट नाम के लिए, आप अपवर्जन सूची एक आईडी से बाहर रहते हैं। यदि, उपरोक्त उदाहरण की तरह, डुप्लिकेट CompanyName = DoubleData Co.और आईडी के साथ 4 पंक्तियाँ हैं 4,6,8,9, तो बहिष्करण सूची में इन ID में से केवल 3 होनी चाहिए।

दूसरे दृष्टिकोण के साथ एक और नुकसान बोझिल स्थिति है (कितना बोझिल इस बात पर निर्भर करता है कि पहली बार में कितने डुप्लिकेट हैं), चूंकि SQL-Server फ़िल्टर किए गए अनुक्रमित NOT INके WHEREहिस्से में ऑपरेटर का समर्थन नहीं करता है। एसक्यूएल-फिडल देखें । इसके बजाय WHERE (CompanyID NOT IN (3,7,4,6,8,9)), आपको कुछ ऐसा करना होगा जैसे कि WHERE (CompanyID <> 3 AND CompanyID <> 7 AND CompanyID <> 4 AND CompanyID <> 6 AND CompanyID <> 8 AND CompanyID <> 9)मुझे यकीन नहीं है कि ऐसी स्थिति के साथ दक्षता के निहितार्थ हैं, अगर आपके पास सैकड़ों डुप्लिकेट नाम हैं।


एक अन्य समाधान (@Alex Kuznetsov के समान) एक और कॉलम जोड़ने के लिए है, इसे रैंक संख्याओं के साथ पॉप्युलेट करें और इस कॉलम सहित एक अद्वितीय सूचकांक जोड़ें:

ALTER TABLE Company
  ADD Rn TINYINT DEFAULT 1;

UPDATE x
SET Rn = Rnk
FROM
  ( SELECT 
      CompanyID,
      Rn,
      Rnk = ROW_NUMBER() OVER (PARTITION BY CompanyName 
                               ORDER BY CompanyID)
    FROM Company 
  ) x ;

CREATE UNIQUE INDEX CompanyName_UQ 
  ON Company (CompanyName, Rn) ; 

फिर, डुप्लिकेट नाम के साथ एक पंक्ति सम्मिलित करना DEFAULT 1संपत्ति और अद्वितीय सूचकांक के कारण विफल हो जाएगा । यह अभी भी 100% मूर्ख नहीं है (जबकि एलेक्स है)। यदि विवरण Rnमें स्पष्ट रूप से सेट किया गया INSERTहै या Rnमान दुर्भावनापूर्ण रूप से अपडेट किए गए हैं , तो डुप्लिकेट अभी भी खिसक जाएगा ।

एसक्यूएल-फिडल -2


-2

एक अन्य विकल्प एक स्केलर फ़ंक्शन लिखना है जो यह जांचता है कि क्या मान पहले से ही तालिका में मौजूद है और फिर उस फ़ंक्शन को एक चेक बाधा से कॉल करें।

यह प्रदर्शन के लिए भयानक चीजें करेगा।



हारून द्वारा बताए गए मुद्दों के अलावा, उत्तर यह नहीं बताता है कि इस चेक बाधा को कैसे जोड़ा जा सकता है, इसलिए यह मौजूदा डुप्लिकेट की उपेक्षा करता है।
ypercube y

-2

मैं उसी की खोज कर रहा हूं - एक अविशिष्ट अद्वितीय सूचकांक बनाएं ताकि मौजूदा खराब डेटा को नजरअंदाज कर दिया जाए, लेकिन नए रिकॉर्ड किसी भी चीज के डुप्लिकेट नहीं हो सकते हैं जो पहले से मौजूद हैं।

इस धागे को पढ़ते समय, यह मेरे पास आता है कि एक बेहतर समाधान एक ट्रिगर लिखना है जो डुप्लिकेट के लिए मूल तालिका के खिलाफ [सम्मिलित] की जांच करेगा, और यदि कोई डुप्लिकेट उन तालिकाओं के बीच मौजूद है, तो रोलबैक TRAN।

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