2016-04-07 3 views
0

Ich muss eine Methode zum Erstellen einer eindeutigen Bestellnummer erstellen. Jede Bestellnummer muss immer größer als die letzte sein, sie sollte jedoch nicht immer fortlaufend sein. Die Lösung muss in einer Webfarmumgebung funktionieren.Bestellnummer mit einer Stored-Prozedur erstellen auswählen und innerhalb einer Transaktion aktualisieren

Derzeit haben Sie eine gespeicherte Prozedur, die für das Abrufen einer neuen Bestellnummer verantwortlich ist, die so gesetzt werden muss, dass die Bestellnummer nicht fortlaufend ist. Die Anwendung wechselt jetzt von einem einzelnen Server zu einer Webfarm und daher ist die Kontrolle des Zugriffs auf die gespeicherte Prozedur über eine Sperre in C# als Methode zur Zugriffskontrolle nicht mehr möglich. Ich habe die gespeicherte Prozedur wie folgt aktualisiert, aber ich bin besorgt, dass ich blocks \ locks \ deadlocks einführen werde, wenn gleichzeitige Aufrufe auftreten.

der Tabelle und Indexstrukturen sind als

MyAppSetting Tabelle

CREATE TABLE [dbo].[MyAppSetting](
     [SettingName] [nvarchar](255) NOT NULL, 
     [SettingValue] [nvarchar](max) NOT NULL, 
    CONSTRAINT [PK_MyAppSetting] PRIMARY KEY CLUSTERED 
    (
     [SettingName] ASC 
    )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] 
    ) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY] 

Meine Bestellung Tisch

CREATE TABLE [dbo].[MyOrder](
     [id] [int] IDENTITY(1,1) NOT NULL, 
     [OrderNumber] [nvarchar](50) NOT NULL CONSTRAINT [DF_MyOrder_OrderNumber] DEFAULT (N''), 
     ... rest of the table 
    CONSTRAINT [PK_MyOrder] PRIMARY KEY NONCLUSTERED 
    (
     [id] ASC 
    )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] 
    ) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY] 

Die SQL-Transaktion

Set Transaction Isolation Level Serializable; 

    Begin Transaction 

     --Gen random number 
     SELECT @Random = ROUND(((@HighSeed - @LowSeed -1) * RAND() + @LowSeed), 0) 

     --Get Seed 
     select @Seed = [SettingValue] FROM [MyAppSetting] where [SettingName] = 'OrderNumberSeed' 

     --Removed concurrency and not required as order numbe should not exceed the seed number 
     --select @MaxOrderNUmber = Max(OrderNumber) FROM MyOrder 

     --if @MaxOrderNumber >= @Seed Begin 
     -- Set @Seed = @MaxOrderNumber 
     --end 

     -- New Seed 
     Set @OrderNumber = @Seed + @Random 

     Update [MyAppSetting] Set [SettingValue] = @OrderNumber where [SettingName] = 'OrderNumberSeed' 

     select @OrderNumber 

    Commit 
+1

Fragen Sie, ob dies Deadlocks verursachen kann? Wenn dies der Fall ist, hängt die Antwort davon ab, was sonst noch auf diese Tabellen zugreift, aber ich glaube nicht, dass diese Prozedur sich selbst blockieren würde. Wenn Sie nachfragen, ob es bessere Möglichkeiten gibt, die Bestellnummer zu generieren, müssten Sie die Anforderung klarer beschreiben. – Rattle

+2

Es scheint mir, dass dieses Skript eine Nebenläufigkeitsproblem hat. Ich denke, Sie müssen bvc_Order sperren, BEVOR Sie MAX erhalten und sperren, bis Sie die bvc_WebappSetting-Tabelle aktualisieren. – FLICKER

+0

@Rattle, guter Punkt Ich habe meine Anforderungen hinzugefügt – Stig

Antwort

0

Mit dem überarbeiteten folgt Wenn Sie SQL angegeben haben, müssen Sie nur eine Tabelle auswählen und aktualisieren. Sie können dies in einer einzigen Abfrage tun, die das Risiko von Deadlocks vermeidet und die Notwendigkeit einer expliziten Transaktion vermeidet.

Setup:

CREATE TABLE OrderNumber (NextOrderNumber int) 
INSERT OrderNumber(NextOrderNumber) values (123) 

Get Next Bestellnummer

DECLARE @MinIncrement int = 5 
DECLARE @MaxIncrement int = 50 
DECLARE @Random int = ROUND(((@MaxIncrement - @MinIncrement -1) * RAND() + @MinIncrement), 0) 
DECLARE @OrderNumber int 

UPDATE OrderNumber 
SET @OrderNumber=NextOrderNumber, NextOrderNumber = NextOrderNumber + @Random 

SELECT @OrderNumber 

I LowSeed und HighSeed zu MinIncrement und MaxIncrement geändert, wie ich den Begriff Seed, hier zu sein verwirrend gefunden. Ich würde eine Tabelle verwenden, um die Bestellnummer zu verfolgen, um zu verhindern, dass irgendetwas in der MyAppSetting-Tabelle gesperrt wird.

Ich würde auch die Anforderung einer Reihenfolge, die immer zunimmt, aber nicht sequentiell herausfordern - ohne dies wäre eine GUID einfacher. Alternativen zu berücksichtigen wäre, die Bestellnummer aus der Zeit irgendwie abgeleitet zu haben - mit letzter Ziffer, um verschiedene Server zu identifizieren.