#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()