2012-01-21 13 views
11

बनाने के लिए मुझे SQL Server 2008 कॉलम में एक पूर्णांक बढ़ाने की आवश्यकता है।SQL सर्वर 2008 में MAX + 1 पूर्णांक के साथ समवर्ती समस्याओं से बचें ... स्वयं पहचान मान

लगता है जैसे मुझे IDENTITY कॉलम का उपयोग करना चाहिए, लेकिन मुझे अपने प्रत्येक ग्राहक के लिए अलग काउंटर बढ़ाने की आवश्यकता है। ई-कॉमर्स साइट के बारे में सोचें जहां प्रत्येक ग्राहक को अपना बढ़ता क्रम संख्या प्राप्त होता है, जो 1 से शुरू होता है। मूल्य अद्वितीय (प्रति ग्राहक) होना चाहिए।

उदाहरण के लिए

,

Customer1 (Order #s 1,2,3,4,5...) 
Customer2 (Order #s 1,2,3,4,5...) 

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

मैं काफी सहज कर रहा हूँ:

BEGIN TRANSACTION 
    SELECT @NewOrderNumber = MAX(OrderNumber)+1 From Orders where [email protected] 
    INSERT INTO ORDERS VALUES (@NewOrderNumber, other order columns here) 
COMMIT TRANSACTION 

मेरे समस्या ताला लगा है और संगामिति चिंताओं और एक अनूठा मूल्य आश्वस्त। ऐसा लगता है कि हमें TABLOCKX से लॉक करने की आवश्यकता है। लेकिन यह एक उच्च मात्रा डेटाबेस है और जब भी मुझे SELECT MAX+1 प्रक्रिया करने की आवश्यकता होती है और एक नया ऑर्डर रिकॉर्ड डालने की आवश्यकता होती है तो मैं पूरी Orders तालिका को लॉक नहीं कर सकता।

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

तो लॉकिंग पद्धति या तकनीक डेडलॉक्स से कैसे बचेंगी और फिर भी मुझे प्रति ग्राहक अद्वितीय वृद्धि क्रम संख्या बनाए रखने दें?

+0

संभव डुप्लिकेट: एक प्राप्त करने के लिए पर्याप्त एक transcation में इस घोंसले है डेटाबेस से अद्वितीय संख्या?] (http://stackoverflow.com/questions/4169591/sql-server-is-this-nesting-in-a-transcation-sfulicient-for-getting-a-unique-num) – GSerg

उत्तर

0

प्रत्येक ग्राहक के लिए पहचान फ़ील्ड के साथ एक टेबल बनाना संभव होगा, तो आप ग्राहक की तालिका में एक नया रिकॉर्ड डाल सकते हैं और उस से मूल्य खींच सकते हैं।

+0

एंटनी , मैं प्रत्येक ग्राहक के लिए अलग पहचान फ़ील्ड नहीं बना सकता क्योंकि ग्राहकों की संख्या बड़ी है और हमेशा बढ़ रही है। – stackonfire

7

मैं ऑर्डर पीढ़ी के साथ उसी लेनदेन में क्वेरी और अद्यतन करने के लिए प्रति ग्राहक पर अंतिम संख्या रखने के लिए एक तालिका पेश करूंगा। चयन पर

TABLE CustomerNextOrderNumber 
{ 
    CustomerID id PRIMARY KEY, 
    NextOrderNumber int 
} 

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

BEGIN TRANSACTION 

DECLARE @NextOrderNumber INT 

SELECT @NextOrderNumber = NextOrderNumber 
FROM CustomerNextOrderNumber (UPDLOCK) 
WHERE CustomerID = @CustomerID 

UPDATE CustomerNextOrderNumber 
SET NextOrderNumber = NextOrderNumber + 1 
WHERE CustomerID = @CustomerID 


... use number here 


COMMIT 

ही तरह, लेकिन अधिक स्पष्ट दृष्टिकोण (जोआचिम Isaksson से प्रेरित) अद्यतन ताला यहाँ पहली अद्यतन द्वारा लगाए गए है। के खिलाफ प्रेत पढ़ता

BEGIN TRANSACTION 

DECLARE @NextOrderNumber INT 

UPDATE CustomerNextOrderNumber 
SET NextOrderNumber = NextOrderNumber + 1 
WHERE CustomerID = @CustomerID 

SELECT @NextOrderNumber = NextOrderNumber 
FROM CustomerNextOrderNUmber 
where CustomerID = @CustomerID 

...

COMMIT 
+2

या "अपडेट करें ग्राहक नेक्स्टऑर्डर नम्बर सेट NextOrderNumber = NextOrderNumber + 1 आउटपुट डाला गया। अगला ऑर्डर नम्बर जहां ग्राहक आईडी =?" –

+0

@ जोचिम इस्क्ससन हां, यह भी काम करेगा .. – alexm

+0

@ जोचिम इस्क्ससन: इसके बारे में सोचने के बाद मुझे आपके दृष्टिकोण को और अधिक पसंद है :) – alexm

2

डिफ़ॉल्ट लेन-देन के स्तर, read committed, आप की रक्षा नहीं करता।

BEGIN TRANSACTION 
SELECT @NewOrderNumber = MAX(OrderNumber)+1 From Orders where [email protected] 
INSERT INTO ORDERS VALUES (@NewOrderNumber, other order columns here) 
COMMIT TRANSACTION 

यहां तक ​​कि एक स्तर अधिक है, repeatable पढ़ा, तो आप की रक्षा नहीं करता: एक प्रेत पढ़ा जब किसी अन्य प्रक्रिया अपने select और insert के बीच में एक पंक्ति सम्मिलित करता है। केवल उच्चतम अलगाव स्तर, धारावाहिक, प्रेत के खिलाफ सुरक्षा करता है।

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE 
BEGIN TRANSACTION 
... 

एक अन्य समाधान तालिका संशोधित कर सकते हैं यकीन है कि केवल अपने लेन-देन करने के लिए tablockx, holdlock and updlock table hints उपयोग करने के लिए है:

तो एक समाधान उच्चतम अलगाव स्तर है। पहले टेबल को ताला लगाता है, दूसरा लेनदेन के अंत तक लॉक रखता है, और तीसरा चयन के लिए update लॉक करता है, इसलिए इसे बाद में अपग्रेड नहीं करना पड़ता है। यदि आप CustomerID पर एक सूचकांक है

SELECT @NewOrderNumber = MAX(OrderNumber)+1 
From Orders with (tablockx, holdlock, updlock) 
where [email protected] 

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

+0

एंडोमर, उत्तर के लिए धन्यवाद। तनाव परीक्षण के दौरान, हमें सर्जिकल परीक्षण करते समय बहुत अधिक डेडलॉक्स प्राप्त हुए। यही कारण है कि हमने टैबब्लैक की कोशिश की। हमने होल्डॉक के साथ परीक्षण नहीं किया। हमारे पास वास्तव में ग्राहक आईडी पर एक सूचकांक होगा लेकिन पता है कि बैच प्रोसेसिंग में ऑर्डर के बैच इनपुट करते समय हमारे पास प्रति सेकंड 10 ऑर्डर होंगे और कुछ ग्राहक समान होंगे। – stackonfire

+0

अच्छी तरह से 'होल्डॉक' के बिना 'tablockx' deadlocks के लिए दरवाजा खुलता है: तालिका लॉक 'चयन' और' अद्यतन' के बीच जारी किया जाएगा। तो 'होल्डॉक' के साथ परीक्षणों का प्रयास करें। आप 'updlock' क्वेरी संकेत के साथ भी परीक्षण कर सकते हैं, हालांकि' tablockx' पहले से ही उस क्षेत्र को कवर करना चाहिए। – Andomar

3

आप ऐसा कर सकता है:

BEGIN TRANSACTION 
    SELECT ID 
    FROM Customer WITH(ROWLOCK) 
    WHERE Customer.ID = @ID 

    SELECT @NewOrderNumber = MAX(OrderNumber)+1 From Orders where [email protected] 
    INSERT INTO ORDERS VALUES (@NewOrderNumber, other order columns here) 
COMMIT TRANSACTION 

अब हम केवल ग्राहकों को मेज से एक ग्राहक और नहीं सभी ग्राहकों, लॉक कर जब भी 2 लोगों को एक ही समय में एक ही ग्राहक के लिए आदेश जोड़ने की कोशिश, जो कोई भी ग्राहक को लॉक मिल जाता है पहले जीतता है और दूसरे व्यक्ति को इंतजार करना होगा।

यदि लोग अलग-अलग ग्राहकों के लिए ऑर्डर डाल रहे हैं, तो वे एक दूसरे के रास्ते में नहीं पहुंचेंगे!

यह इस तरह काम करेगा है:

  • User 1 आईडी के साथ ग्राहक के लिए एक आदेश को सम्मिलित करने के शुरू 1000
  • User 2 आईडी 1000
  • User 2 करने के लिए है के साथ ग्राहक के लिए एक आदेश डालने की कोशिश करता है उपयोगकर्ता 1 ऑर्डर डालने तक प्रतीक्षा करें।
  • उपयोगकर्ता 1 आदेश डालें और लेनदेन प्रतिबद्ध है।
  • User 2 अब आदेश सम्मिलित कर सकते हैं और ग्राहक 1000.
+0

धन्यवाद। क्या आप समझा सकते हैं कि आपको अपनी पहली क्वेरी क्यों चाहिए (ग्राहक से आईडी का चयन करें (rowlock) जहां ग्राहक[email protected]) बिल्कुल? लॉकिंग चिंता ऑर्डर तालिका पर है और यहां तक ​​कि चयन कथन केवल एक ग्राहक आईडी तक सीमित है। – stackonfire

+0

@ स्टैकोनफायर, मैं कुछ और स्पष्टीकरण जोड़ता हूं, अगर मुझे अभी भी स्पष्ट नहीं है तो मुझे बताएं। –

0

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

भले ही आपको यह काम मिल जाए। क्या होता है यदि ग्राहक ए के पास एक ईलीयर ऑर्डर हटा दिया गया है, तो क्या आप उन्हें अपने सभी मौजूदा रिकॉर्डों को लगातार बनाए रखने और 1 से शुरू करने के लिए जा रहे हैं। अब यह एक लॉकिंग समस्या होगी ....

रिकॉर्ड दें पहचान (या संभवतः एक guid) जब आप एक गिनती चाहते हैं, तो इसके लिए पूछताछ करें, यदि आप पंक्ति संख्या चाहते हैं (कभी भी उस बिंदु को कभी नहीं देखा), rowno का उपयोग करें।

आपको प्रति ग्राहक एक ऑटो incrementing आदेश की आवश्यकता नहीं है, आप एक नहीं चाहते हैं, और बड़ी मात्रा में लॉकिंग के बिना एक नहीं हो सकता है।

पार्श्व सोच समय।

यदि आप पेश

Order Description Date Due 
1  Staples  26/1/2012 
2  Stapler  1/3/2012 
3  Paper Clips 19/1/2012 

यह मतलब नहीं है (और वास्तव इसका मतलब यह नहीं करना चाहिए में) है कि आदेश चाबियाँ 1, 2 और 3 रहे हैं, वे कुछ भी जब तक वे एक विशिष्टता को पूरा के रूप में किया जा सकता है आवश्यकता।

+0

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

+1

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

0
create table TestIds 
(customerId int, 
nextId int) 

insert into TestIds 
values(1,1) 
insert into TestIds 
values(2,1) 
insert into TestIds 
values(3,1) 

go 

create proc getNextId(@CustomerId int) 
as 

declare @NextId int 

while (@@ROWCOUNT = 0) 
begin 
    select @NextId = nextId 
    from TestIds 
    where customerId = @CustomerId 

    update TestIds 
    set nextId = nextId + 1 
    where customerId = @CustomerId 
    and nextId = @NextId 

end 

select @NextId 
go 
3

एसक्यूएल सर्वर 2005 में और बाद में, यह सबसे अच्छा, atomically किये गये किसी भी लेन-देन का उपयोग कर या ताला लगा के बिना है:

update ORDERS 
set OrderNumber=OrderNumber+1 
output inserted.OrderNumber where [email protected] 
[एसक्यूएल सर्वर
संबंधित मुद्दे