2010-06-07 16 views
7

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

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

प्रत्येक आदेश विक्रेता-विशिष्ट है। असल में, मेरे पास OrderNumberInfo (VendorID, OrderNumber) तालिका है। अब जब भी कोई ग्राहक ऑर्डर देता है तो मुझे कण विक्रेता के लिए OrderNumber बढ़ाने और उस मान को वापस करने की आवश्यकता होती है। स्वाभाविक रूप से, मैं अन्य प्रक्रियाओं मुझे के साथ हस्तक्षेप नहीं करना चाहते, तो मैं विशेष रूप से इस पंक्ति किसी भी तरह लॉक करने की आवश्यकता:

begin tranaction 

    declare @n int 
    select @n = OrderNumber 
     from OrderNumberInfo 
     where VendorID = @vendorID 

    update OrderNumberInfo 
     set OrderNumber = @n + 1 
     where OrderNumber = @n and VendorID = @vendorID 

commit transaction 

अब, मैं select ... with (updlock rowlock) के बारे में, निराशावादी ताला, आदि पढ़ा है, लेकिन सिर्फ फिट नहीं कर सकते यह सब एक सुसंगत तस्वीर में:

  • ये संकेत SQL सर्वर 2008 के स्नैपशॉट अलगाव के साथ कैसे खेलते हैं?
  • क्या वे पंक्ति-स्तर, पृष्ठ-स्तर या यहां तक ​​कि तालिका-स्तर के ताले भी करते हैं?
  • यह एक विक्रेता के लिए संख्याएं उत्पन्न करने की कोशिश कर रहे एकाधिक उपयोगकर्ताओं को कैसे सहन करता है?
  • क्या अलगाव स्तर यहां उचित हैं?
  • और आम तौर पर - ऐसी चीजों को करने का तरीका क्या है?

संपादित

बस कुछ चीजें स्पष्ट करने के:

एप्लिकेशन के इस विशेष कोने में
  • प्रदर्शन बिल्कुल नहीं एक मुद्दा है: आदेश अपेक्षाकृत कम रखा जाएगा और एक शामिल होगी विक्रेताओं की वेब सेवा के लिए महंगी कॉल, इसलिए 1-सेकंड देरी काफी सहनशील है
  • हम वास्तव में को प्रत्येक विक्रेता के ऑर्डर नंबरों को बी करने की आवश्यकता है ई स्वतंत्र और अनुक्रमिक

उत्तर

3

आप OUTPUT खंड का उपयोग कर सकते हैं। लेनदेन की आवश्यकता के बिना इसे सभी परमाणु रूप से करना चाहिए।

-- either return the order number directly as a single column resultset 
UPDATE OrderNumberInfo 
SET OrderNumber = OrderNumber + 1 
    OUTPUT DELETED.OrderNumber 
WHERE VendorID = @vendorID 


-- or use an intermediate table variable to get the order number into @n 
DECLARE @n INT 
DECLARE @temp TABLE (OrderNumber INT) 

UPDATE OrderNumberInfo 
SET OrderNumber = OrderNumber + 1 
    OUTPUT DELETED.OrderNumber 
    INTO @temp (OrderNumber) 
WHERE VendorID = @vendorID 

SET @n = (SELECT TOP 1 OrderNumber FROM @temp) 

उपरोक्त उदाहरण मान VendorID स्तंभ एक अद्वितीय बाधा है, या बहुत कम से कम वहाँ केवल प्रति विक्रेता ID एक पंक्ति हो जाएगा कि कि। यदि ऐसा नहीं है तो आप संभावित रूप से अद्यतन और/या एकाधिक पंक्तियों को वापस कर देंगे, जो एक अच्छा विचार नहीं लग रहा है!

+0

यह दिलचस्प है! धन्यवाद! –

+0

यदि ऑर्डर नम्बर कॉलम अंतिम उपयोग ऑर्डर नम्बर का प्रतिनिधित्व करता है, तो क्या आप सम्मिलित नहीं होंगे। ऑर्डर नम्बर? – Thomas

+0

@ थॉमस: मैं सिर्फ प्रश्न में दिए गए कोड के व्यवहार की नकल कर रहा हूं, जो ऑर्डर नंबर पहले प्राप्त करता है और फिर कॉलम को बढ़ाता है। लेकिन अगर आप बढ़ाना चाहते हैं और फिर बढ़ी हुई ऑर्डर नंबर प्राप्त करें तो 'INSERTED.OrderNumber' ऐसा करने का तरीका होगा। – LukeH

4

आपका समाधान OrderNumberInfo तालिका पर संभावित प्रदर्शन बाधा उत्पन्न करेगा।

क्या कोई विशिष्ट कारण है कि आदेश केवल पहचान कॉलम नहीं हो सकते हैं, संभवतः आवेदन पक्ष (जैसे एमएसएफटी -232323) पर एक विक्रेता आईडी के साथ prefixed?

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

वे अभी भी बढ़ेगा और अद्वितीय प्रति-विक्रेता जो ऑर्डर आईडी के लिए एकमात्र वास्तविक आवश्यकता है।

यह निश्चित रूप से बहुत आसान विक्रेता-स्वतंत्र तर्क का अतिरिक्त साइड लाभ होगा, जो आपके पास कभी भी है) - जैसे एप्लिकेशन-व्यापी क्यूसी/रिपोर्टिंग।

+0

दुर्भाग्य से, यह _is_ आवश्यकता है: प्रत्येक विक्रेता को एक स्वतंत्र अनुक्रम होना चाहिए। –

+2

@ एंटन - ठीक है ... घने होने के लिए खेद है, लेकिन इस आवश्यकता के लिए स्रोत/व्यावसायिक कारण क्या है? – DVK

+2

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

1

मैं सामान्य रूप से कुछ इस तरह का उपयोग करें:

update OrderNumberInfo with (rowlock) 
set @OrderNumber = OrderNumber, OrderNumber = OrderNumber + 1 
where VendorID = @VendorID 

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

मुझे विश्वास है (लेकिन साबित नहीं हुआ है) कि SQL सर्वर इसे परमाणु बनाने के लिए एक लेनदेन के बजाय एक लोच का उपयोग करता है, जो अधिक कुशल होना चाहिए।

तो अपनी मेज डिजाइन ऐसी है कि विक्रेता पंक्ति मांग पर बनाया जाना है, तो यह मौजूद नहीं है की जरूरत है, तो बजाय इस तर्क का उपयोग करें:

declare @error int, @rowcount int 

-- Attempt to read and update the number. 
update OrderNumberInfo with (rowlock) 
set @OrderNumber = OrderNumber, OrderNumber = OrderNumber + 1 
where VendorID = @VendorID 

select @error = @@error, @rowcount = @@rowcount 
if @error <> 0 begin 
    return @error 
end 

-- If the update succeeded then exit now. 
if @rowcount > 0 begin 
    return 0 
end 

-- Insert the row if it doesn't exist yet. 
insert into OrderNumberInfo (VendorID, OrderNumber) 
select VendorID, 1 
where not exists (select null from OrderNumberInfo where VendorID = @VendorID) 

select @error = @@error 
if @error <> 0 begin 
    return @error 
end 

-- Attempt to read and update the number. 
update OrderNumberInfo with (rowlock) 
set @OrderNumber = OrderNumber, OrderNumber = OrderNumber + 1 
where VendorID = @VendorID 

select @error = @@error 
if @error <> 0 begin 
    return @error 
end 

इस कोड को अभी भी एक लेन-देन की आवश्यकता नहीं है, क्योंकि प्रत्येक परमाणु कथन इस बात पर ध्यान दिए बिना कि कितने अन्य कनेक्शन कोड को एक साथ निष्पादित कर रहे हैं।

अस्वीकरण: मैंने एसक्यूएल सर्वर 7-2005 पर समस्याओं के बिना इसका उपयोग किया है। मैं अभी तक 2008 में

0

तरह से अपने व्यवहार पर टिप्पणी नहीं कर सकता यह करने के लिए स्थिरता बनाए रखने के लिए:

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE 
BEGIN TRANSACTION 
declare @n int 
select @n = OrderNumber 
    from OrderNumberInfo 
    where VendorID = @vendorID 

update OrderNumberInfo 
    set OrderNumber = @n + 1 
    where OrderNumber = @n and VendorID = @vendorID 

COMMIT TRANSACTION 

इस अलगाव की कठोरतम प्रपत्र का उपयोग करेगा और कोई अजीब व्यापार को सुनिश्चित करेगा।

0

यहां यह है:

@C int = 0 घोषित करें; अद्यतन तालिका सेट कोड = @ सी, @ सी = @ सी + 1