एक गैर-अद्वितीय सूचकांक पर डुप्लिकेट कुंजी पंक्ति नहीं डाल सकता है?


14

हमने पिछले कुछ दिनों में तीन बार इस अजीब त्रुटि का सामना किया है, 8 सप्ताह के लिए त्रुटि मुक्त होने के बाद, और मैं स्टम्प्ड हूं।

यह त्रुटि संदेश है:

Executing the query "EXEC dbo.MergeTransactions" failed with the following error:
"Cannot insert duplicate key row in object 'sales.Transactions' with unique index
'NCI_Transactions_ClientID_TransactionDate'.
The duplicate key value is (1001, 2018-12-14 19:16:29.00, 304050920).".

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

यह सबसे हालिया लिंक है जो मुझे मिल सकता है जिसमें मेरे मुद्दे हैं लेकिन मुझे कोई समाधान नहीं दिख रहा है।

https://www.sqlservercentral.com/forums/topic/error-cannot-insert-duplicate-key-row-in-a-non-unique-index

मेरे परिदृश्य के बारे में कुछ बातें:

  • खरीद TransactionID (प्राथमिक कुंजी का हिस्सा) को अपडेट कर रही है - मुझे लगता है कि यह वही है जो त्रुटि पैदा कर रहा है लेकिन पता नहीं क्यों? हम उस तर्क को हटा देंगे।
  • तालिका में परिवर्तन ट्रैकिंग सक्षम है
  • लेनदेन करना पढ़े बिना पढ़ा हुआ

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

तालिका

USE [DB]
GO

/****** Object:  Table [sales].[Transactions]    Script Date: 5/29/2019 1:37:49 PM ******/
SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[sales].[Transactions]') AND type in (N'U'))
BEGIN
CREATE TABLE [sales].[Transactions]
(
    [TransactionID] [bigint] NOT NULL,
    [ClientID] [int] NOT NULL,
    [TransactionDate] [datetime2](2) NOT NULL,
    /* snip*/
    [BusinessUserID] [varchar](150) NOT NULL,
    [BusinessTransactionID] [varchar](150) NOT NULL,
    [InsertDate] [datetime2](2) NOT NULL,
    [UpdateDate] [datetime2](2) NOT NULL,
 CONSTRAINT [PK_Transactions_TransactionID] PRIMARY KEY CLUSTERED 
(
    [TransactionID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, DATA_COMPRESSION=PAGE) ON [DB_Data]
) ON [DB_Data]
END
GO
USE [DB]

IF NOT EXISTS (SELECT * FROM sys.indexes WHERE object_id = OBJECT_ID(N'[sales].[Transactions]') AND name = N'NCI_Transactions_ClientID_TransactionDate')
begin
CREATE NONCLUSTERED INDEX [NCI_Transactions_ClientID_TransactionDate] ON [sales].[Transactions]
(
    [ClientID] ASC,
    [TransactionDate] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, DATA_COMPRESSION = PAGE) ON [DB_Data]
END

IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[sales].[DF_Transactions_Units]') AND type = 'D')
BEGIN
ALTER TABLE [sales].[Transactions] ADD  CONSTRAINT [DF_Transactions_Units]  DEFAULT ((0)) FOR [Units]
END
GO

IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[sales].[DF_Transactions_ISOCurrencyCode]') AND type = 'D')
BEGIN
ALTER TABLE [sales].[Transactions] ADD  CONSTRAINT [DF_Transactions_ISOCurrencyCode]  DEFAULT ('USD') FOR [ISOCurrencyCode]
END
GO

IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[sales].[DF_Transactions_InsertDate]') AND type = 'D')
BEGIN
ALTER TABLE [sales].[Transactions] ADD  CONSTRAINT [DF_Transactions_InsertDate]  DEFAULT (sysdatetime()) FOR [InsertDate]
END
GO

IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[sales].[DF_Transactions_UpdateDate]') AND type = 'D')
BEGIN
ALTER TABLE [sales].[Transactions] ADD  CONSTRAINT [DF_Transactions_UpdateDate]  DEFAULT (sysdatetime()) FOR [UpdateDate]
END
GO

अस्थायी तालिका

same columns as the mgdata. including the relevant fields. Also has a non-unique clustered index
(
    [BusinessTransactionID] [varchar](150) NULL,
    [BusinessUserID] [varchar](150) NULL,
    [PostalCode] [varchar](25) NULL,
    [TransactionDate] [datetime2](2) NULL,

    [Units] [int] NOT NULL,
    [StartDate] [datetime2](2) NULL,
    [EndDate] [datetime2](2) NULL,
    [TransactionID] [bigint] NULL,
    [ClientID] [int] NULL,

) 

CREATE CLUSTERED INDEX ##workingTransactionsMG_idx ON #workingTransactions (TransactionID)

It is populated in batches (500k rows at a time), something like this
IF OBJECT_ID(N'tempdb.dbo.#workingTransactions') IS NOT NULL DROP TABLE #workingTransactions;
select fields 
into #workingTransactions
from import.Transactions
where importrowid between two number ranges -- pseudocode

प्राथमिक कुंजी

 CONSTRAINT [PK_Transactions_TransactionID] PRIMARY KEY CLUSTERED 
(
    [TransactionID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, DATA_COMPRESSION=PAGE) ON [Data]
) ON [Data]

गैर-संकुलित सूचकांक

CREATE NONCLUSTERED INDEX [NCI_Transactions_ClientID_TransactionDate] ON [sales].[Transactions]
(
    [ClientID] ASC,
    [TransactionDate] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, DATA_COMPRESSION = PAGE)

नमूना अद्यतन विवरण

-- updates every field
update t 
set 
    t.transactionid = s.transactionid,
    t.[CityCode]=s.[CityCode],
      t.TransactionDate=s.[TransactionDate],
     t.[ClientID]=s.[ClientID],
                t.[PackageMonths] = s.[PackageMonths],
                t.UpdateDate = @UpdateDate
              FROM #workingTransactions s
              JOIN [DB].[sales].[Transactions] t 
              ON s.[TransactionID] = t.[TransactionID]
             WHERE CAST(HASHBYTES('SHA2_256 ',CONCAT( S.[BusinessTransactionID],'|',S.[BusinessUserID],'|', etc)
                <> CAST(HASHBYTES('SHA2_256 ',CONCAT( T.[BusinessTransactionID],'|',T.[BusinessUserID],'|', etc)

मेरा सवाल यह है कि हुड के नीचे क्या चल रहा है? और उपाय क्या है? संदर्भ के लिए, ऊपर दिए गए लिंक में यह उल्लेख है:

इस बिंदु पर, मेरे पास कुछ सिद्धांत हैं:

  • मेमोरी प्रेशर या बड़े समानांतर अपडेट प्लान से संबंधित बग, लेकिन मैं एक अलग प्रकार की त्रुटि की उम्मीद करूंगा और अब तक मैं कम संसाधनों को सहसंबंधित नहीं कर सकता हूं और इन पृथक और छिटपुट त्रुटियों का समय-समय पर समाधान होगा।
  • UPDATE स्टेटमेंट या डेटा का एक बग प्राथमिक कुंजी पर वास्तविक डुप्लिकेट उल्लंघन का कारण बन रहा है, लेकिन कुछ अस्पष्ट SQL सर्वर बग में परिणाम और त्रुटि संदेश है जो गलत इंडेक्स नाम का हवाला देता है।
  • डर्टी रीड अनकम्यूटेड आइसोलेशन के परिणामस्वरूप पढ़ता है जिससे डबल इंसर्शन के लिए एक बड़ा समानांतर अपडेट होता है। लेकिन ईटीएल डेवलपर्स दावा करते हैं कि डिफ़ॉल्ट रीड कमिट का उपयोग किया जाता है, और यह निर्धारित करना कठिन है कि प्रक्रिया वास्तव में किस अलगाव स्तर पर रनटाइम में उपयोग की जाती है।

मुझे संदेह है कि अगर मैं निष्पादन योजना को एक काम के आसपास, शायद MAXDOP (1) संकेत या स्पूल ऑपरेशन को अक्षम करने के लिए सत्र ट्रेस ध्वज का उपयोग करता हूं, तो त्रुटि अभी दूर हो जाएगी, लेकिन यह स्पष्ट नहीं है कि यह प्रदर्शन को कैसे प्रभावित करेगा

संस्करण

Microsoft SQL Server 2017 (RTM-CU13) (KB4466404) - 14.0.3048.4 (X64) Nov 30 2018 12:57:58 कॉपीराइट (C) 2017 Microsoft कॉर्पोरेशन एंटरप्राइज संस्करण (64-बिट) विंडोज सर्वर 2016 स्टैंडर्ड 10.0 (बिल्ड 14393) पर :)

जवाबों:


10

मेरा सवाल यह है कि हुड के नीचे क्या चल रहा है? और उपाय क्या है?

यह एक बग है। समस्या यह है कि यह केवल कभी-कभी होता है, और प्रजनन के लिए कठिन होगा। फिर भी, आपका सबसे अच्छा मौका Microsoft समर्थन को संलग्न करना है। अद्यतन प्रसंस्करण मन-झुकने वाला जटिल है, इसलिए इसे बहुत विस्तृत जांच की आवश्यकता होगी।

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

निर्धारक अद्यतन लिखें

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

अद्यतन कार्रवाई के मानदंड प्रदान करने के लिए FROM क्लॉज को निर्दिष्ट करते समय सावधानी बरतें। एक अद्यतन कथन के परिणाम अपरिभाषित होते हैं यदि कथन में एक FROM क्लॉज शामिल होता है जो इस तरह से निर्दिष्ट नहीं होता है कि अद्यतन किए जाने वाले प्रत्येक स्तंभ के लिए केवल एक मान उपलब्ध है, तो यह है कि यदि UPDATE कथन निर्धारक नहीं है।

आपका UPDATEहै निर्धारित करने योग्य नहीं है, और परिणाम इसलिए कर रहे हैं अपरिभाषित । आपको इसे बदलना चाहिए ताकि प्रत्येक लक्ष्य पंक्ति के लिए अधिकतम एक स्रोत पंक्ति की पहचान हो। उस परिवर्तन के बिना, अपडेट का परिणाम किसी भी व्यक्तिगत स्रोत पंक्ति को प्रतिबिंबित नहीं कर सकता है ।

उदाहरण

आइए मैं आपको एक उदाहरण दिखाता हूं, प्रश्न में दिए गए उन पर तालिकाबद्ध रूप से मॉडलिंग की गई है:

CREATE TABLE dbo.Transactions
(
    TransactionID bigint NOT NULL,
    ClientID integer NOT NULL,
    TransactionDate datetime2(2) NOT NULL,

    CONSTRAINT PK_dbo_Transactions
        PRIMARY KEY CLUSTERED (TransactionID),

    INDEX dbo_Transactions_ClientID_TranDate
        (ClientID, TransactionDate)
);

CREATE TABLE #Working
(
    TransactionID bigint NULL,
    ClientID integer NULL,
    TransactionDate datetime2(2) NULL,

    INDEX cx CLUSTERED (TransactionID)
);

चीजों को सरल रखने के लिए, लक्ष्य तालिका में एक पंक्ति और स्रोत में चार पंक्तियाँ रखें:

INSERT dbo.Transactions 
    (TransactionID, ClientID, TransactionDate)
VALUES 
    (1, 1, '2019-01-01');

INSERT #Working 
    (TransactionID, ClientID, TransactionDate)
VALUES 
    (1, 2, NULL),
    (1, NULL, '2019-03-03'),
    (1, 3, NULL),
    (1, NULL, '2019-02-02');

सभी चार स्रोत पंक्तियाँ लक्ष्य से मेल खाती हैं TransactionID, इसलिए यदि हम एक अपडेट चलाते हैं (जैसे प्रश्न में एक) जो TransactionIDअकेले में शामिल होता है?

UPDATE T
SET T.TransactionID = W.TransactionID,
    T.ClientID = W.ClientID,
    T.TransactionDate = W.TransactionDate
FROM #Working AS W
JOIN dbo.Transactions AS T
    ON T.TransactionID = W.TransactionID;

( TransactionIDकॉलम को अपडेट करना डेमो के लिए महत्वपूर्ण नहीं है, आप चाहें तो इसे टिप्पणी कर सकते हैं।)

पहला आश्चर्य यह है कि UPDATEलक्ष्य तालिका के बिना किसी भी कॉलम में नल की अनुमति नहीं देने के बावजूद (सभी उम्मीदवार पंक्तियों में एक नल होता है) बिना त्रुटि के पूरा होता है।

महत्वपूर्ण बिंदु यह है कि परिणाम अपरिभाषित है , और इस मामले में एक परिणाम उत्पन्न होता है जो स्रोत पंक्तियों में से किसी से भी मेल नहीं खाता है:

SELECT
    T.TransactionID,
    T.ClientID,
    T.TransactionDate
FROM dbo.Transactions AS T;
╔═══════════════╦══════════╦════════════════════════╗
║ TransactionID ║ ClientID ║    TransactionDate     ║
╠═══════════════╬══════════╬════════════════════════╣
║             1 ║        2 ║ 2019-03-03 00:00:00.00 ║
╚═══════════════╩══════════╩════════════════════════╝

db <> फिडेल डेमो

अधिक विवरण: कोई भी एग्रीगेट टूटा हुआ है

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

एक बोनस के रूप में, आप पा सकते हैं कि निर्धारक बनने के लिए आपके वर्तमान अद्यतन को फिर से लिखना आपकी सामयिक बग समस्या को भी दूर करेगा। उत्पाद बग अभी भी उन लोगों के लिए मौजूद होगा जो पाठ्यक्रम के गैर-निर्धारक अपडेट लिखते हैं।

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