Sqlserver
 sql >> Baza danych >  >> RDS >> Sqlserver

Jak uzyskać następną liczbę w sekwencji

Jeśli nie prowadzisz tabeli liczników, masz dwie możliwości. W ramach transakcji najpierw wybierz MAX(seq_id) z jedną z poniższych wskazówek dotyczących tabeli:

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

TABLOCKX + HOLDLOCK to trochę przesada. Blokuje zwykłe instrukcje select, które można uznać za ciężkie nawet jeśli transakcja jest niewielka.

ROWLOCK, XLOCK, HOLDLOCK wskazówka tabeli jest prawdopodobnie lepszym pomysłem (ale:przeczytaj alternatywę z tabelą licznika dalej). Zaletą jest to, że nie blokuje zwykłych instrukcji select, tj. gdy instrukcje select nie pojawiają się w SERIALIZABLE transakcji lub gdy instrukcje select nie zawierają tych samych wskazówek dotyczących tabeli. Używanie ROWLOCK, XLOCK, HOLDLOCK nadal będzie blokować instrukcje wstawiania.

Oczywiście musisz mieć pewność, że żadna inna część twojego programu nie wybiera MAX(seq_id) bez tych wskazówek tabeli (lub poza SERIALIZABLE transakcji), a następnie użyj tej wartości do wstawienia wierszy.

Należy zauważyć, że w zależności od liczby wierszy, które są w ten sposób zablokowane, możliwe jest, że SQL Server przekształci blokadę w blokadę tabeli. Przeczytaj więcej o eskalacji blokady tutaj .

Procedura wstawiania przy użyciu WITH(ROWLOCK, XLOCK, HOLDLOCK) wyglądałby następująco:

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

Alternatywą i prawdopodobnie lepszym pomysłem jest posiadanie licznika tabeli i podaj te wskazówki na stole licznika. Ta tabela wyglądałaby następująco:

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

Następnie zmienisz procedurę wstawiania w następujący sposób:

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

Zaletą jest to, że używa się mniej blokad wierszy (tj. jedna na model w dbo.counter_seq ), a eskalacja blokady nie może zablokować całego pliku dbo.table_seq tabeli, blokując w ten sposób polecenia select.

Możesz to wszystko przetestować i samemu zobaczyć efekty, umieszczając WAITFOR DELAY '00:01:00' po wybraniu sekwencji z counter_seq i majstrowanie przy tabelach w drugiej zakładce SSMS.

PS1:Używanie ROW_NUMBER() OVER (PARTITION BY model ORDER BY ID) nie jest dobrym sposobem. Jeśli wiersze zostaną usunięte/dodane lub zmienione zostaną identyfikatory, kolejność ulegnie zmianie (należy wziąć pod uwagę identyfikatory faktury, które nigdy nie powinny się zmieniać). Również pod względem wydajności konieczność określania numerów wszystkich poprzednich wierszy podczas pobierania pojedynczego wiersza jest złym pomysłem.

PS2:Nigdy nie używałbym zewnętrznych zasobów do blokowania, gdy SQL Server już zapewnia blokowanie poprzez poziomy izolacji lub szczegółowe wskazówki dotyczące tabel.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Integracja SQL Service Broker i NServiceBus

  2. SQL Server:obliczanie zakresów dat

  3. System.AccessViolationException w .NET 4.0 podczas łączenia się z bazą danych SQL

  4. Jak mogę pominąć nawiasy kwadratowe w klauzuli LIKE?

  5. System.Data.SqlTypes.SqlTypeException:przepełnienie SqlDateTime