2015-01-15 50 views
2

我有一個類似於C#的POS系統,並且很長一段時間它不會出現任何問題(它只是一個POS)。但在這幾天中,有4個POS使用該系統,並且連接到同一個數據庫,並且一個POS的所有銷售都轉到同一個審計(表),其中所有其他銷售都去了。 因此,在這個系統中,這是程序由於延遲,避免SQL服務器中的重複

  1. 函數來獲得最後的票號(與簡單的SELECT)
  2. 添加1至數(下一tickt無)。
  3. 生成一個將此票號(包含終端,日期和員工代碼)注入算法的ID代碼
  4. 將銷售記錄插入數據庫,包含所有必要信息(日期,客戶端,員工,IDCode等) )(用簡單的INSERT INTO)

但是有4個POS我意識到有些銷售有相同的票號,幸運的是票號代碼是不一樣的,因爲終端和僱員是不同的,但是如何可以避免這一點?

編輯1: 每個POS系統具有雙重功能,在一個模式中,POS銷售是集中式的,而在這種模式下每個POS產生連續的門票(如他們所有,其中一個POS),在其他模式每個POS有自己的票證分號,因此我不能使用這個身份。

+1

你最好展示你的代碼。 – dario 2015-01-15 18:22:02

+5

數據庫具有「身份」列,可在單個原子操作中爲您生成下一個數字。不要在多線程環境中自己生成它,因爲您無法保證兩個操作不會同時發生。讓數據庫生成標識符。 – David 2015-01-15 18:22:59

+0

不要以編程方式創建記錄標識符 - 讓數據庫處理此問題(如David所述),並使用事務處理關鍵操作。 – Filburt 2015-01-15 18:23:46

回答

3

只需使用一個序列來生成下一個票號。

CREATE SEQUENCE Tickets 
    START WITH 1 
    INCREMENT BY 1; 

然後每個POS只是做

SELECT NEXT VALUE FOR Tickets; 

的順序是保證永遠不會返回相同數量的兩倍。

+0

這就是我所說的優雅的解決方案!並解決我遇到的其他問題。 – 2015-01-18 18:38:44

+0

@CesarRomeroo這很好。由於某種原因,我完全忘記了SEQUENCE ;-)。我只想提到,如果你不能在ID號碼中出現空白,那麼你需要進行測試,因爲我似乎記得它的工作類似於IDENTITY,因爲如果INSERT由於任何原因失敗,那麼該值已經遞增下一次。不要誤解我的意思,如果這樣做有效,那麼一定要用我推薦的方法。只要做足夠的測試以確保它確實按照您的預期工作,特別是在非理想情況下。但+1不管:-)。 – 2015-01-19 19:32:13

+0

如果INSERT失敗,我正在考慮使用ALTER SECUENCE,可能會起作用,但(假設大量銷售的極端情況)能否產生明顯的延遲,從而導致出現問題? – 2015-01-20 17:41:04

0

您需要在原子動作中執行此操作。因此,您可以將所有內容都包含在事務中並鎖定表。 See here關於鎖定等方面的一個很好的討論。

鎖定會放慢一切,因爲一切都將開始等待表釋放完成,這可能不是你可以忍受的。

或者你可以應該在數據庫管理的列上使用identity,並保持唯一的遞增數字。

你也可以創建你的主鍵(希望你有一個)是幾件事的組合。然後,您可以爲每個POS端點保留一個運行號碼,以查看更多有關他們如何執行的數據。但是,這更多地涉及分析,這不在範圍內。

0

我強烈建議您不要使用當前的方法,如果可以的話更改爲GUID PK。

但是,我意識到在某些情況下,重新設計是不可能的(我們有與您在舊數據庫中描述的完全相同的場景)。

在這種情況下,你可以放心地使用UPDLOCK表提示結合INSERT命令得到最大的價值和使用OUTPUT INSERTED功能檢索新的主鍵值到一個局部變量如果需要的話:

DECLARE 
    @PK Table (PK INT NOT NULL) 

INSERT 
    INTO Audit (
     TicketNumber, 
     Terminal, 
     Date, 
     EmployeeCode, 
     Client, 
     IDCode, 
     ... other fields) 
    /* Record the new PK in the tablevariable */ 
OUTPUT INSERTED.TicketNumber INTO @PK 
SELECT IsNull(MAX(TicketNumber), 0) + 1, 
     @Terminal, 
     @Date, 
     @EmployeeCode, 
     @Client, 
     @IDCode, 
     ... other values 
    FROM Audit WITH (UPDLOCK) 

DECLARE @TicketNumber INT 

    /* Move the new PK from the local tablevariable into a local variable for subsequent use */ 
SELECT @TicketNumber = PK 
    FROM @PK 
+0

這種生成非IDENTITY ID並立即檢索它們的方法在引入了'OUTPUT'子句的SQL Server 2005中已經過時。您只需將'OUTPUT INSERTED.TicketNumber INTO TempTableOrTableVariable(FieldName)'添加到插入。除此之外,他們還引入了'NEWSEQUENTIALID()',它可以減少由於GUID的隨機性而導致的碎片化。我會_never_推薦一個GUID PK,特別是如果PK是聚集的。如果您需要GUID,請將其設爲唯一的非聚集索引。使用它來查找應用程序,但是使用CLUSTER INT PK作爲JOIN等。 – 2015-01-15 19:03:08

+0

這是一個很好的可能性,但爲了澄清,這是假裝首先插入所有其他票據數據,然後獲取可用的tick number? – 2015-01-15 20:33:20

+0

@CesarRomeroo:不,在這種方法中,您同時插入票證數據並在一次交易中獲取下一個可用密鑰。我忘記了struzky提到的OUTPUT INSERTED機制,這將使操作更加高效。我將很快用這個信息更新答案,並添加一些示例列來顯示完整的概念。 – 2015-01-15 22:14:50

1

如前所述,如果TicketNumber是連續且唯一的,那麼聽起來就像一個IDENTITY字段是要走的路。但是,如果由於某種原因阻止了某些事情發生,或者如果此時需要進行太多更改,則可以通過使用應用程序通過在ID代碼生成過程本身上創建鎖來將流程本身限制爲單線程鎖(見sp_getapplocksp_releaseapplock)。應用程序鎖可讓您圍繞任意概念創建鎖。意思是,你可以將@Resource定義爲「generate_id_code」,這將強制每個呼叫者等待輪到它。這將遵循這種結構:

BEGIN TRANSACTION; 
    EXEC sp_getapplock @Resource = 'generate_id_code', @LockMode = 'Exclusive'; 

    ...current 4 steps to generate the ID Code... 

    EXEC sp_releaseapplock @Resource = 'generate_id_code'; 
    COMMIT TRANSACTION; 

你需要管理的錯誤/回滾自己(作爲鏈接的MSDN文檔中所述),以便把在平時的try/catch。但是,這確實可以讓你管理這種情況。

請注意:sp_getapplock/sp_releaseapplock應該謹慎使用;應用程序鎖定一定非常方便(比如像這樣的情況),但只有在絕對必要時才能使用它們。

+0

這是否甚至會鎖定SELECT命令(在收回最後一個Ticket號碼的同時已經被處理並且正在處理其他Ticket的意圖)? – 2015-01-15 20:23:42

+1

@CesarRomeroo它不會鎖定命令,它會將訪問鎖定到另一個進程請求的相同'@ Resource'值的點之外,無論是使用這個相同的存儲過程還是另一個也使用'sp_getapplock'的@Resource價值。這就是它的美妙之處,它是守門員,不需要在表格中鎖定行。請記住,使「ID代碼生成」過程單線程會稍微減慢它,但這是您請求的固有部分,如果您將IDENTITY字段添加到表中,代替價值。 – 2015-01-15 20:30:12

+0

@CesarRomeroo意思是,如果所有4個POS系統同時調用這個相同的進程,只有其中一個可以獲得該鎖。另外3人將等待這一個完成並釋放鎖定,此時另一個人將獲得它,另外兩個人將等待。但是當其他3個人在等待的時候,他們不會被阻止發出SELECT,他們只是不能進入具有SELECT的行。所以其他進程可以從該表中自由選擇。 – 2015-01-15 20:34:41