sql >> Database teknologi >  >> RDS >> Sqlserver

Sådan får du det næste tal i en rækkefølge

Hvis du ikke vedligeholder et bordbord, er der to muligheder. Inden for en transaktion skal du først vælge MAX(seq_id) med et af følgende tabeltip:

  1. WITH(TABLOCKX, HOLDLOCK)
  2. WITH(ROWLOCK, XLOCK, HOLDLOCK)

TABLOCKX + HOLDLOCK er lidt overkill. Det blokerer almindelige udvalgte sætninger, som kan betragtes som tunge selvom transaktionen er lille.

En ROWLOCK, XLOCK, HOLDLOCK bordtip er nok en bedre idé (men:læs alternativet med et bordbord længere fremme). Fordelen er, at den ikke blokerer for almindelige select-sætninger, dvs. når select-sætningerne ikke vises i en SERIALIZABLE transaktion, eller når de udvalgte udsagn ikke giver de samme tabeltip. Brug ROWLOCK, XLOCK, HOLDLOCK vil stadig blokere indsæt sætninger.

Du skal selvfølgelig være sikker på, at ingen andre dele af dit program vælger MAX(seq_id) uden disse tabeltip (eller uden for en SERIALIZABLE transaktion), og brug derefter denne værdi til at indsætte rækker.

Bemærk, at afhængigt af antallet af rækker, der er låst på denne måde, er det muligt, at SQL Server vil eskalere låsen til en tabellås. Læs mere om låseeskalering her .

Indsættelsesproceduren ved hjælp af WITH(ROWLOCK, XLOCK, HOLDLOCK) ville se ud som følger:

DECLARE @target_model INT=3;
DECLARE @part VARCHAR(128)='Spine';
BEGIN TRY
    BEGIN TRANSACTION;
    DECLARE @max_seq INT=(SELECT MAX(seq) FROM dbo.table_seq WITH(ROWLOCK,XLOCK,HOLDLOCK) WHERE [email protected]_model);
    IF @max_seq IS NULL SET @max_seq=0;
    INSERT INTO dbo.table_seq(part,seq,model)VALUES(@part,@max_seq+1,@target_model);
    COMMIT TRANSACTION;
END TRY
BEGIN CATCH
    ROLLBACK TRANSACTION;
END CATCH

Et alternativ og sandsynligvis en bedre idé er at have en tæller bord, og giv disse bordtips på bordbordet. Denne tabel vil se sådan ud:

CREATE TABLE dbo.counter_seq(model INT PRIMARY KEY, seq_id INT);

Du vil derefter ændre indsætningsproceduren som følger:

DECLARE @target_model INT=3;
DECLARE @part VARCHAR(128)='Spine';
BEGIN TRY
    BEGIN TRANSACTION;
    DECLARE @new_seq INT=(SELECT seq FROM dbo.counter_seq WITH(ROWLOCK,XLOCK,HOLDLOCK) WHERE [email protected]_model);
    IF @new_seq IS NULL 
        BEGIN SET @new_seq=1; INSERT INTO dbo.counter_seq(model,seq)VALUES(@target_model,@new_seq); END
    ELSE
        BEGIN SET @new_seq+=1; UPDATE dbo.counter_seq SET [email protected]_seq WHERE [email protected]_model; END
    INSERT INTO dbo.table_seq(part,seq,model)VALUES(@part,@new_seq,@target_model);
    COMMIT TRANSACTION;
END TRY
BEGIN CATCH
    ROLLBACK TRANSACTION;
END CATCH

Fordelen er, at der bruges færre rækkelåse (dvs. én pr. model i dbo.counter_seq ), og låseeskalering kan ikke låse hele dbo.table_seq tabel blokerer således udvalgte udsagn.

Du kan teste alt dette og selv se effekterne ved at placere en WAITFOR DELAY '00:01:00' efter at have valgt sekvensen fra counter_seq , og fifler med tabellen/tabellerne i en anden SSMS-fane.

PS1:Brug af ROW_NUMBER() OVER (PARTITION BY model ORDER BY ID) er ikke en god måde. Hvis rækker slettes/tilføjes, eller ID'er ændres, ændres rækkefølgen (overvej faktura-id'er, der aldrig bør ændres). Også med hensyn til ydeevne at skulle bestemme rækkenumrene for alle tidligere rækker, når du henter en enkelt række, er det en dårlig idé.

PS2:Jeg ville aldrig bruge eksterne ressourcer til at levere låsning, når SQL Server allerede tilbyder låsning gennem isolationsniveauer eller finmaskede tabeltip.



  1. Adgang nægtet for brugeren 'www-data'@'localhost - hvordan skal man håndtere det?

  2. Er venstre ydre sammenføjninger og venstre sammenføjninger det samme?

  3. MySQL High Availability Framework Forklaret – Del I:Introduktion

  4. Reverse engineering (oracle) skema til ERD