Sqlserver
 sql >> база данни >  >> RDS >> Sqlserver

Как да получите следващото число в редица

Ако не поддържате маса за брояч, има два варианта. В рамките на транзакция първо изберете MAX(seq_id) с една от следните таблични подсказки:

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

TABLOCKX + HOLDLOCK е малко пресилено. Той блокира обикновените оператори за избиране, които могат да се считат за тежки въпреки че транзакцията е малка.

A ROWLOCK, XLOCK, HOLDLOCK намек за таблица вероятно е по-добра идея (но:прочетете алтернативата с таблица за брояч по-нататък). Предимството е, че не блокира обикновените оператори за избор, т.е. когато операторите за избор не се появяват в SERIALIZABLE транзакция или когато операторите за избор не предоставят едни и същи съвети за таблица. Използване на ROWLOCK, XLOCK, HOLDLOCK пак ще блокира изрази за вмъкване.

Разбира се, трябва да сте сигурни, че никоя друга част от вашата програма не избира MAX(seq_id) без тези таблични подсказки (или извън SERIALIZABLE транзакция) и след това използвайте тази стойност за вмъкване на редове.

Имайте предвид, че в зависимост от броя на редовете, които са заключени по този начин, е възможно SQL Server да ескалира заключването до заключване на таблица. Прочетете повече за ескалацията на заключване тук .

Процедурата за вмъкване използва WITH(ROWLOCK, XLOCK, HOLDLOCK) ще изглежда по следния начин:

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

Алтернатива и вероятно по-добра идея е да имате брояч маса и дайте тези подсказки за масата на масата за брояч. Тази таблица ще изглежда по следния начин:

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

След това бихте променили процедурата за вмъкване, както следва:

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

Предимството е, че се използват по-малко заключвания на редове (т.е. по едно на модел в dbo.counter_seq ), а ескалацията на заключване не може да заключи целия dbo.table_seq таблица, като по този начин блокира избраните оператори.

Можете да тествате всичко това и сами да видите ефектите, като поставите WAITFOR DELAY '00:01:00' след избиране на последователността от counter_seq , и си играете с таблицата(ите) във втори раздел SSMS.

PS1:Използване на ROW_NUMBER() OVER (PARTITION BY model ORDER BY ID) не е добър начин. Ако редовете са изтрити/добавени или идентификаторите са променени, последователността ще се промени (помислете за идентификатори на фактури, които никога не трябва да се променят). Също така по отношение на производителността, необходимостта да се определят номерата на редовете на всички предишни редове при извличане на един ред е лоша идея.

PS2:Никога не бих използвал външни ресурси, за да осигуря заключване, когато SQL Server вече осигурява заключване чрез нива на изолация или фини съвети за таблица.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Съхранена процедура, лявото присъединяване ми дава дублиращи се редове, но искам горен (1) ред за всеки запис със същия идентификатор

  2. Как да конвертирате DateTime в TimeSpan в заявка на Entity Framework

  3. Правилно използване на транзакциите в SQL Server

  4. Обединете два дяла в един в SQL Server (T-SQL)

  5. Генериране на **голи** CREATE TABLE и CREATE PROC изрази в SQL Server