मेरे पास एक मेज है जो IDENTITY
विभिन्न अन्य तालिकाओं में फ़ील्ड के विकल्प के रूप में एक विरासत अनुप्रयोग द्वारा उपयोग की जाती है ।
तालिका में प्रत्येक पंक्ति LastID
नाम के क्षेत्र के लिए अंतिम उपयोग की गई आईडी को संग्रहीत करती है IDName
।
कभी-कभी संग्रहीत खरीद में गतिरोध हो जाता है - मुझे विश्वास है कि मैंने एक उपयुक्त त्रुटि हैंडलर बनाया है; हालाँकि मुझे यह देखने में दिलचस्पी है कि क्या यह कार्यप्रणाली काम करती है जैसा कि मुझे लगता है कि यह करता है, या अगर मैं यहाँ गलत पेड़ को भौंक रहा हूं।
मुझे पूरा यकीन है कि बिना किसी गतिरोध के इस तालिका तक पहुंचने का एक तरीका होना चाहिए।
डेटाबेस के साथ ही कॉन्फ़िगर किया गया है READ_COMMITTED_SNAPSHOT = 1
।
सबसे पहले, यहाँ तालिका है:
CREATE TABLE [dbo].[tblIDs](
[IDListID] [int] NOT NULL
CONSTRAINT PK_tblIDs
PRIMARY KEY CLUSTERED
IDENTITY(1,1) ,
[IDName] [nvarchar](255) NULL,
[LastID] [int] NULL,
);
और IDName
क्षेत्र पर गैर-अनुक्रमित सूचकांक :
CREATE NONCLUSTERED INDEX [IX_tblIDs_IDName]
ON [dbo].[tblIDs]
(
[IDName] 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
, FILLFACTOR = 80
);
GO
कुछ नमूना डेटा:
INSERT INTO tblIDs (IDName, LastID)
VALUES ('SomeTestID', 1);
INSERT INTO tblIDs (IDName, LastID)
VALUES ('SomeOtherTestID', 1);
GO
संग्रहीत कार्यविधि तालिका में संग्रहीत मानों को अद्यतन करने के लिए उपयोग की जाती है, और अगली आईडी लौटाती है:
CREATE PROCEDURE [dbo].[GetNextID](
@IDName nvarchar(255)
)
AS
BEGIN
/*
Description: Increments and returns the LastID value from tblIDs
for a given IDName
Author: Max Vernon
Date: 2012-07-19
*/
DECLARE @Retry int;
DECLARE @EN int, @ES int, @ET int;
SET @Retry = 5;
DECLARE @NewID int;
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
SET NOCOUNT ON;
WHILE @Retry > 0
BEGIN
BEGIN TRY
BEGIN TRANSACTION;
SET @NewID = COALESCE((SELECT LastID
FROM tblIDs
WHERE IDName = @IDName),0)+1;
IF (SELECT COUNT(IDName)
FROM tblIDs
WHERE IDName = @IDName) = 0
INSERT INTO tblIDs (IDName, LastID)
VALUES (@IDName, @NewID)
ELSE
UPDATE tblIDs
SET LastID = @NewID
WHERE IDName = @IDName;
COMMIT TRANSACTION;
SET @Retry = -2; /* no need to retry since the operation completed */
END TRY
BEGIN CATCH
IF (ERROR_NUMBER() = 1205) /* DEADLOCK */
SET @Retry = @Retry - 1;
ELSE
BEGIN
SET @Retry = -1;
SET @EN = ERROR_NUMBER();
SET @ES = ERROR_SEVERITY();
SET @ET = ERROR_STATE()
RAISERROR (@EN,@ES,@ET);
END
ROLLBACK TRANSACTION;
END CATCH
END
IF @Retry = 0 /* must have deadlock'd 5 times. */
BEGIN
SET @EN = 1205;
SET @ES = 13;
SET @ET = 1
RAISERROR (@EN,@ES,@ET);
END
ELSE
SELECT @NewID AS NewID;
END
GO
संग्रहित खरीद का नमूना निष्पादन:
EXEC GetNextID 'SomeTestID';
NewID
2
EXEC GetNextID 'SomeTestID';
NewID
3
EXEC GetNextID 'SomeOtherTestID';
NewID
2
संपादित करें:
मैंने एक नया सूचकांक जोड़ा है, क्योंकि मौजूदा सूचकांक IX_tblIDs_Name का उपयोग SP द्वारा नहीं किया जा रहा है; मुझे लगता है कि क्वेरी प्रोसेसर क्लस्टर्ड इंडेक्स का उपयोग कर रहा है क्योंकि इसे लास्टआईडी में संग्रहीत मूल्य की आवश्यकता है। वैसे भी, यह सूचकांक वास्तविक निष्पादन योजना द्वारा उपयोग किया जाता है:
CREATE NONCLUSTERED INDEX IX_tblIDs_IDName_LastID
ON dbo.tblIDs
(
IDName ASC
)
INCLUDE
(
LastID
)
WITH (FILLFACTOR = 100
, ONLINE=ON
, ALLOW_ROW_LOCKS = ON
, ALLOW_PAGE_LOCKS = ON);
EDIT # 2:
मैंने सलाह ली है कि @AaronBertrand ने दिया और इसे थोड़ा संशोधित किया। यहां सामान्य विचार अनावश्यक लॉकिंग को खत्म करने के लिए बयान को परिष्कृत करने और समग्र रूप से सपा को अधिक कुशल बनाने के लिए है।
नीचे दिया गया कोड ऊपर दिए गए कोड को निम्न से बदल देता BEGIN TRANSACTION
है END TRANSACTION
:
BEGIN TRANSACTION;
SET @NewID = COALESCE((SELECT LastID
FROM dbo.tblIDs
WHERE IDName = @IDName), 0) + 1;
IF @NewID = 1
INSERT INTO tblIDs (IDName, LastID)
VALUES (@IDName, @NewID);
ELSE
UPDATE dbo.tblIDs
SET LastID = @NewID
WHERE IDName = @IDName;
COMMIT TRANSACTION;
चूँकि हमारा कोड कभी भी इस तालिका में एक रिकॉर्ड नहीं जोड़ता है जिसमें 0 में LastID
हम यह अनुमान लगा सकते हैं कि यदि @NewID 1 है, तो इरादा सूची में एक नई आईडी संलग्न करता है, अन्यथा हम सूची में एक मौजूदा पंक्ति को अपडेट कर रहे हैं।
SERIALIZABLE
यहां तक बढ़ रहे हैं।