2012-03-19 14 views
6

मेरे पास अनुक्रमिक संख्याओं (चालान संख्या या छात्र आईडी) के साथ एक तालिका है।डेटाबेस के समवर्ती उपयोग - दो उपयोगकर्ताओं को एक ही मान प्राप्त करने से रोकना

किसी बिंदु पर, उपयोगकर्ता को पिछले नंबर का अनुरोध करने की आवश्यकता है (अगली संख्या की गणना करने के लिए)। एक बार उपयोगकर्ता वर्तमान नंबर जानता है, तो उन्हें अगले नंबर जेनरेट करने और तालिका में जोड़ने की आवश्यकता है।

मेरी चिंता यह है कि दो उपयोगकर्ता समवर्ती पहुंच के कारण दो समान संख्याओं को गलती से उत्पन्न करने में सक्षम होंगे।

मैंने संग्रहित प्रक्रियाओं के बारे में सुना है, और मुझे पता है कि यह एक समाधान हो सकता है। क्या समवर्ती मुद्दों से बचने के लिए यहां एक सर्वोत्तम अभ्यास है?

संपादित: यहाँ क्या मैं अब तक है:

USE [master] 
GO 

SET ANSI_NULLS ON 
GO 
SET QUOTED_IDENTIFIER ON 
GO 
ALTER PROCEDURE [dbo].[sp_GetNextOrderNumber] 
AS 
BEGIN 
    BEGIN TRAN 

DECLARE @recentYear INT 
DECLARE @recentMonth INT 
DECLARE @recentSequenceNum INT 

-- SET NOCOUNT ON added to prevent extra result sets from 
-- interfering with SELECT statements. 
SET NOCOUNT ON; 

    -- get the most recent numbers 
SELECT @recentYear = Year, @recentMonth = Month, @recentSequenceNum = OrderSequenceNumber 
FROM dbo.OrderNumbers 
WITH (XLOCK) 
WHERE Id = (SELECT MAX(Id) FROM dbo.OrderNumbers) 

    // increment the numbers 
IF (YEAR(getDate()) > IsNull(@recentYear,0)) 
    BEGIN 
     SET @recentYear = YEAR(getDate()); 
     SET @recentMonth = MONTH(getDate()); 
     SET @recentSequenceNum = 0; 
    END 
ELSE 
    BEGIN 
     IF (MONTH(getDate()) > IsNull(@recentMonth,0)) 
      BEGIN 
       SET @recentMonth = MONTH(getDate()); 
       SET @recentSequenceNum = 0; 
      END 
     ELSE 
      SET @recentSequenceNum = @recentSequenceNum + 1; 
    END 

-- insert the new numbers as a new record 
INSERT INTO dbo.OrderNumbers(Year, Month, OrderSequenceNumber) 
VALUES (@recentYear, @recentMonth, @recentSequenceNum) 

COMMIT TRAN 
END 

यह काम करने के लिए लगता है, और मुझे मूल्यों मैं चाहता हूँ देता है। अभी तक, मैंने समवर्ती पहुंच को रोकने के लिए अभी तक कोई लॉकिंग नहीं जोड़ा है।

संपादित करें 2: लेनदेन पूरा होने तक तालिका को लॉक करने के लिए WITH(XLOCK) जोड़ा गया। मैं यहां प्रदर्शन के लिए नहीं जा रहा हूं। जब तक मुझे डुप्लिकेट प्रविष्टियां नहीं मिलतीं, और डेडलॉक्स नहीं होते हैं, तो यह काम करना चाहिए।

उत्तर

6

आप जानते हैं कि SQL सर्वर आपके लिए यह करता है, है ना? यदि आपको किसी अन्य के आधार पर नए मान की गणना करने की आवश्यकता है तो आपको अनुक्रमिक संख्या या गणना कॉलम की आवश्यकता होने पर आप एक पहचान कॉलम कर सकते हैं।

लेकिन, यदि यह आपकी समस्या का समाधान नहीं करता है, या यदि आपको अपनी नई संख्या उत्पन्न करने के लिए जटिल गणना करने की आवश्यकता है जो एक साधारण डालने में नहीं किया जा सकता है, तो मैं एक संग्रहित प्रक्रिया लिखने का सुझाव देता हूं जो तालिका को ताला लगाता है अंतिम मूल्य, नया उत्पन्न करता है, इसे सम्मिलित करता है और फिर तालिका को अनलॉक करता है।

लेनदेन अलगाव स्तर

के बारे में जानने के लिए इस link पढ़ें सिर्फ "ताला" अवधि संभव के रूप में छोटे

+0

जो संख्या मैं उत्पन्न करूँगा वह वर्ष (3 अंक), महीने (2 अंक) के बाद, उसके बाद 3 अंकों की संख्या होती है जो हर महीने शून्य पर रीसेट हो जाती है। तो 012 03 000 की तरह कुछ। –

+0

इस बात पर निर्भर करता है कि वह 3 अंकों का नंबर कहां से आ रहा है, आप आसानी से गणना किए गए कॉलम के साथ इसे हल कर सकते हैं। अन्यथा, एक प्रक्रिया – Diego

+0

का उपयोग करें 3 अंकों की संख्या केवल पिछले संख्या से बढ़ी है (जब तक कि पिछले नंबर पिछले महीने से नहीं है, तब संख्या 0 फिर से है) –

0

जिस तरह से हम इसे SQL सर्वर में संभालते हैं, वह एक ही लेनदेन के भीतर UPDLOCK तालिका संकेत का उपयोग कर रहा है।

उदाहरण के लिए:

INSERT 
    INTO MyTable (
     MyNumber , 
     MyField1) 
    SELECT IsNull(MAX(MyNumber), 0) + 1 , 
     "Test" 
    FROM MyTable WITH (UPDLOCK) 

यह बहुत नहीं है, लेकिन क्योंकि हम डेटाबेस डिजाइन प्रदान किया गया है और यह कारण लीगेसी एप्लिकेशन डाटाबेस को ऐक्सेस करने के लिए नहीं बदल सकते हैं, यह सबसे अच्छा समाधान है कि हम साथ आने कर सकता था।

+0

संग्रहीत प्रक्रिया के रूप में निष्पादित? –

+0

हमारा हैं, लेकिन यह निश्चित रूप से एक आवश्यकता नहीं है। यह एसक्यूएल है जो मायने रखता है, न कि इसे कैसे निष्पादित किया जाता है। –

3

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

create table [Counter] 
(
    LastNumber int 
) 

और एकल पंक्ति के साथ प्रारंभ:

insert into [Counter] values(0) 

नमूना चालान तालिका:

create table invoices 
(
    InvoiceID int identity primary key, 
    Number varchar(8), 
    InvoiceDate datetime 
) 

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

create proc LastNumber (@NumberOfNextNumbers int = 1) 
as 
begin 
    declare @LastNumber int 

    update [Counter] 
     set LastNumber = LastNumber + @NumberOfNextNumbers -- Holds update lock 
    select @LastNumber = LastNumber 
    from [Counter] 
    return @LastNumber 
end 

ट्रिगर, एक साथ डाला चालान की संख्या हो जाता है कि संख्या के साथ संग्रहीत प्रक्रिया और अद्यतन चालान से अगले n संख्या पूछता है।

create trigger InvoiceNumberTrigger on Invoices 
after insert 
as 
    set NoCount ON 

    declare @InvoiceID int 
    declare @LastNumber int 
    declare @RowsAffected int 

    select @RowsAffected = count(*) 
    from Inserted 
    exec @LastNumber = dbo.LastNumber @RowsAffected 

    update Invoices 
    -- Year/month parts of number are missing 
     set Number = right ('000' + ltrim(str(@LastNumber - rowNumber)), 3) 
    from Invoices 
     inner join 
     (select InvoiceID, 
       row_number() over (order by InvoiceID desc) - 1 rowNumber 
      from Inserted 
     ) insertedRows 
      on Invoices.InvoiceID = InsertedRows.InvoiceID 

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

उदाहरण:

insert into invoices (invoiceDate) values(GETDATE()) 

के रूप में संख्या स्तंभ के मूल्य ऑटोजनरेटेड है, एक चाहिए यह फिर से पढ़ें। मेरा मानना ​​है कि ईएफ के पास प्रावधान हैं।

संबंधित मुद्दे