Реализация увеличивающегося счетчика в SQL

#sql-server #tsql #transactions #increment

#sql-сервер #tsql #транзакции #увеличить

Вопрос:

Поэтому мне нужно по существу реализовать столбец с автоматическим увеличением. У меня есть столбец в одной таблице, который имеет значение int, которое будет действовать как счетчик, и будет другая таблица с соответствующим количеством строк, каждая строка имеет свой собственный счетчик. Я думал, что это будет довольно просто реализовать, но после проведения некоторых исследований я нашел несколько форумов, где они упоминают, что в подобных ситуациях могут возникнуть проблемы, если несколько транзакций выполняются примерно одновременно, даже при выполнении select, update и insert в одной транзакции

После изучения, хотя мне показалось, что если бы я использовал предложение OUTPUT для получения нового значения, с которым обновляется первая таблица, я мог бы полностью избежать этой проблемы.

Итак, это T-SQL, о котором я думаю. Должно ли это подходить для моей ситуации?

 BEGIN TRANSACTION;

DECLARE @tblCtrs table (Ctr int)
DECLARE @CtrVal INT

UPDATE Table1
SET Ctr = Ctr 1
OUTPUT inserted.Ctr INTO @CtrVals
WHERE id=<whatever id value>

SET @CtrVal = (SELECT TOP(1) Ctr FROM @tblCtrs)

INSERT INTO TABLE2 (Ctr, ....)
VALUES( @CtrVal, ....)

COMMIT;
 

Ответ №1:

Если я правильно понял ваш вопрос, проблема заключается в том, что параллельные транзакции могут привести к непреднамеренным результатам со значением счетчика. Вы прекрасно знаете, как управлять значениями счетчика, это просто вопрос того, может ли какая-то другая транзакция одновременно считывать / изменять. Если это понимание верно, то для этого и существуют уровни изоляции. В зависимости от способа доступа / хранения данных (например, может ли быть несколько записей с одинаковым <любым значением идентификатора> или это уникально?) и требований приложения (например, нормально ли, если <любое значение идентификатора> не уникально, и есть вставка новой записи, которая не обновляется из-за время) вы должны соответствующим образом УСТАНОВИТЬ УРОВЕНЬ ИЗОЛЯЦИИ ТРАНЗАКЦИЙ.

Комментарии:

1. Спасибо, я думаю, что это все. Похоже, ПОВТОРЯЕМОЕ ЧТЕНИЕ — это то, что мне нужно. Очень признателен!

2. Хорошо, я мог бы просто догадаться, но будет ли это все еще необходимо для этой ситуации? Если бы у меня были две из этих транзакций, выполняемых одновременно, первая получила бы блокировку, затем считала исходное значение, увеличивала его, сохраняла, а затем выводила вставленную таблицу в параметр таблицы. Теперь, когда я думаю об этом, оператор Insert даже не обязательно должен находиться в той же транзакции, что и update, если я могу обновить значение таблицы и получить новое значение без вмешательства другого обновления или влияния первой транзакции обновления. Это должно работать правильно?

3. Это вопрос желаемого поведения, когда строки вставляются одновременно. Простой пример: BEGIN TRAN T1 ВЫБЕРИТЕ * ИЗ MyTable, ГДЕ ID=’x’ … <другая работа здесь> … ФИКСИРУЙТЕ сейчас одновременно с <other work> INSERT В ЗНАЧЕНИЯ MyTable (ID, …) (‘x’ …) В зависимости от уровня изоляции вставка может блокироваться или не блокироваться до тех пор, пока T1 не будет зафиксирован или откат. Должно ли это быть? Т.е. нужно ли <other work> работать со всеми записями с ID=’x’ в хронологической области этой транзакции или нормально, если нет? Будет ли нормально иметь ctr с разными значениями для одного и того же идентификатора?

Ответ №2:

Если вы можете использовать столбцы идентификаторов, вы можете использовать SCOPE_IDENTITY(), чтобы найти значение идентификатора последней вставленной записи следующим образом:

 DECLARE 
@ParentData varchar(20) = 'ParentData1',
@ChildData varchar(20) = 'ChildData1',
@ParentID int

DECLARE @ParentTable table (ParentId int IDENTITY(1, 1), ParentData Varchar(20))
DECLARE @ChildTable table (ChildId int IDENTITY(1,1), ParentId int, ChildData varchar(20))

INSERT INTO @ParentTable 
    (ParentData)
VALUES
    (@ParentData)

SET @ParentId = SCOPE_IDENTITY()

INSERT INTO @ChildTable
    (ParentId, ChildData)
VALUES
    (@ParentID, @ChildData)

SET @ChildData = 'ChildData1,1'
INSERT INTO @ChildTable
    (ParentId, ChildData)
VALUES
    (@ParentID, @ChildData)

SET @ParentData = 'ParentData2'
SET @ChildData = 'ChildData2'

INSERT INTO @ParentTable 
    (ParentData)
VALUES
    (@ParentData)

INSERT INTO @ChildTable
    (ParentId, ChildData)
VALUES
    (SCOPE_IDENTITY(), @ChildData)


SELECT * FROM @ParentTable
SELECT * FROM @ChildTable
 

Комментарии:

1. К сожалению, нет. Я действительно рассматривал столбцы идентификаторов, но проблема в том, что в таблице 1 будет строка, и у каждой будет свой счетчик, поэтому в таблице 2 у каждой также будет своя последовательность. Но все равно спасибо. Я всегда забываю о SCOPE_IDENTITY()