sql >> Database >  >> RDS >> Sqlserver

Hoe het volgende nummer in een reeks te krijgen

Als u geen toonbanktafel onderhoudt, zijn er twee opties. Selecteer binnen een transactie eerst de MAX(seq_id) met een van de volgende tabelhints:

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

TABLOCKX + HOLDLOCK is een beetje overdreven. Het blokkeert reguliere select-statements, die als zwaar kunnen worden beschouwd ook al is de transactie klein.

A ROWLOCK, XLOCK, HOLDLOCK tafelhint is waarschijnlijk een beter idee (maar:lees het alternatief met een tellertabel verderop). Het voordeel is dat het reguliere select-statements niet blokkeert, dat wil zeggen wanneer de select-statements niet voorkomen in een SERIALIZABLE transactie, of wanneer de select-instructies niet dezelfde tabelhints bieden. ROWLOCK, XLOCK, HOLDLOCK gebruiken zal nog steeds insert-statements blokkeren.

Natuurlijk moet je er zeker van zijn dat geen andere delen van je programma de MAX(seq_id) selecteren zonder deze tabelhints (of buiten een SERIALIZABLE transactie) en gebruik vervolgens deze waarde om rijen in te voegen.

Houd er rekening mee dat, afhankelijk van het aantal rijen dat op deze manier is vergrendeld, het mogelijk is dat SQL Server de vergrendeling escaleert naar een tabelvergrendeling. Lees hier .

De invoegprocedure met behulp van WITH(ROWLOCK, XLOCK, HOLDLOCK) ziet er als volgt uit:

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

Een alternatief en waarschijnlijk een beter idee is om een ​​teller te hebben tafel, en geef deze tafelhints op de aanrechttafel. Deze tabel ziet er als volgt uit:

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

U zou dan de invoegprocedure als volgt wijzigen:

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

Het voordeel is dat er minder rijvergrendelingen worden gebruikt (dwz één per model in dbo.counter_seq ), en lock escalatie kan de hele dbo.table_seq niet vergrendelen tabel waardoor select-statements worden geblokkeerd.

U kunt dit allemaal testen en zelf de effecten zien door een WAITFOR DELAY '00:01:00' te plaatsen na het selecteren van de reeks uit counter_seq , en spelen met de tafel(s) in een tweede SSMS-tabblad.

PS1:ROW_NUMBER() OVER (PARTITION BY model ORDER BY ID) gebruiken is geen goede manier. Als rijen worden verwijderd/toegevoegd, of ID's worden gewijzigd, verandert de volgorde (denk aan factuur-ID's die nooit mogen veranderen). Ook qua prestaties is het een slecht idee om de rijnummers van alle voorgaande rijen te bepalen bij het ophalen van een enkele rij.

PS2:ik zou nooit externe bronnen gebruiken om vergrendeling te bieden, wanneer SQL Server al vergrendeling biedt via isolatieniveaus of fijnmazige tabelhints.



  1. SELECT werkt niet in node.js

  2. Hoe rijen in te vullen op basis van gebeurtenistypegegevens

  3. Basis DB-verbindingspool met Java en Tomcat 7

  4. Mysql-tabelnaam werkt niet in hoofdletters