2010-06-07 71 views
7

這個問題當然適用於更廣泛的範圍,但在這裏。在SQL Server中生成訂單號的正確方法

我有一個基本的電子商務應用程序,用戶可以很自然地下訂單。所述訂單需要有一個唯一的編號,我現在正在嘗試生成。

每個訂單都是供應商特定的。基本上,我有一個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 Server 2008s的快照隔離?
  • 它們是否執行行級,頁面級甚至表級鎖?
  • 這如何容忍多個用戶試圖爲單個供應商生成數字?
  • 什麼隔離級別適合在這裏?
  • 一般來說 - 做這些事情的方法是什麼?

編輯

只是爲了讓一些事情更清晰:在應用程序中的這個特殊的角落

  • 性能絕對不是一個問題:訂單會相對較少放置,將涉及一昂貴的電話給供應商的網絡服務,所以1秒的延遲是相當可以容忍的
  • 我們真的需要讓每個供應商的訂單號碼爲b e獨立且連續

回答

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

這很有趣!謝謝! – 2010-06-07 15:54:34

+0

如果OrderNumber列表示最後使用的OrderNumber,那麼您是不是需要Inserted.OrderNumber? – Thomas 2010-06-07 16:33:12

+0

@Thomas:我只是模仿問題中給出的代碼的行爲,它先獲取訂單號然後遞增列。但是如果你想增加,然後得到遞增的訂單號,那麼'INSERTED.OrderNumber'就是做這件事的方法。 – LukeH 2010-06-07 16:47:18

4

您的解決方案將在OrderNumberInfo表中創建潛在的性能瓶頸。

是否有任何具體的原因,爲什麼命令不能簡單地作爲身份列,可能在應用程序端用供應商ID作爲前綴(例如MSFT-232323)?

這種方法的唯一缺點是每個供應商的訂單不會是「Add-1-get-next-order-#」模式,但我不知道任何技術或商業考慮因素爲什麼會出現問題,儘管它可能會使順序排序處理稍微複雜一些。

他們仍然會增加和唯一的每個供應商這是訂單ID的唯一真正需求。

假設你有任何一種方法,它當然會有非常簡單的與供應商無關的邏輯的附加好處) - 比如在整個應用程序範圍內的QC /報告。

+0

不幸的是,這是這個要求:每個供應商需要有一個獨立的序列。 – 2010-06-07 15:56:20

+2

@Anton - 好吧...很抱歉,因爲密度大,但是這個要求的來源/商業原因是什麼? – DVK 2010-06-07 15:57:51

+2

同意DVK,你正在做一些完全不需要和有風險的事情。如果我有這個要求,我會推回去告訴他們,風險超過了任何可感知的收益。只要每個供應商都有獨特的orderids,那麼一切都很好,並不像他們會看到彼此的ID,並且實際上沒有有效的商業原因,因爲訂單必須按順序排列,沒有跳過的值。這樣做的額外成本以及數據完整性問題帶來的額外風險(如果您第一次沒有做到這一點)比使用身份領域要高得多,因爲從字面上看沒有任何好處。沒有。 – HLGEM 2010-06-07 16:06:45

1

我通常使用的是這樣的:

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

它不需要被包裹在一個事務中。事實上,如果你把它包裝在一個事務中,那麼SQL Server就會開始在表上保持鎖定並且放慢一切。當我需要在Web服務中這樣做時,我總是在可能在當時打開的任何事務之外的單獨數據庫連接上執行它,只是爲了確保。

我相信(但沒有證明)SQL Server使用閂鎖而不是事務使其成爲原子,這應該是更有效的。

如果您的表設計是這樣的供應商的行必須根據需要創建,如果它不存在,然後用這個邏輯來代替:

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 

此代碼仍然不需要交易,因爲每個原子語句都會成功,無論有多少其他連接同時執行代碼。

聲明:我已經在SQL Server 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

它在這裏:

declare @C int = 0; 更新表設置代碼= @ C,@ C = @ C + 1