2013-09-18 4 views
3

Я реализую конкурс, где может быть много одновременных записей. Я собираю некоторые пользовательские данные, которые я помещаю в одну таблицу с именами. У меня есть другая таблица сгенерированных уникальных скидочных кодов в таблице, называемой discountCodes. Затем я присваиваю их каждой записи. Я думал, что сделаю это, поместив идентификатор записи в таблицу discountCodes.Блокировка MS SQL для обновления

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

Каков наилучший способ гарантировать, что строка не будет назначена дважды?

+0

Вы можете добавить 'falg' к столу, так что вы можете пометить как только один выбрать каждую строку? –

+0

@huMptyduMpty Проблема с этим - если пользователи одновременно выбирают его, они могут выбрать один и тот же флажок перед установкой флага. – Elias

+0

@Elias: Да, но вы можете избежать использования одного и того же твизма строки? –

ответ

1

Что-то может быть сделано как

Следующий пример устанавливает TRANSACTION ISOLATION LEVEL сессии. Для каждого последующего оператора Transact-SQL SQL Server хранит все разделяемые блокировки до конца транзакции.Источник: MSDN

USE databaseName; 
GO 
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ; 
GO 
BEGIN TRANSACTION; 
GO 
SELECT * 
    FROM Table1; 
GO 
SELECT * 
    FROM Table2; 
GO 
COMMIT TRANSACTION; 
GO 

Подробнее SET TRANSACTION ISOLATION LEVEL

0
WITH e AS 
     (
     SELECT *, 
       ROW_NUMBER() OVER (ORDER BY id) rn 
     FROM entries ei 
     WHERE NOT EXISTS 
       (
       SELECT NULL 
       FROM discountCodes dci 
       WHERE dci.entryId = ei.id 
       ) 
     ), 
     dc AS 
     (
     SELECT *, 
       ROW_NUMBER() OVER (ORDER BY id) rn 
     FROM discountCodes 
     WHERE entryId IS NULL 
     ) 
UPDATE dc 
SET  dc.entryId = e.id 
FROM e 
JOIN dc 
ON  dc.rn = e.rn 
+0

Будет ли это автоматически блокироваться, т. Е. Может ли другой поток также выбирать ту же строку между выборами и запуском обновления, если вы запускаете их одновременно? –

+0

@SteveTemple: да, будет.Пока другой сеанс попытается прочитать запись «discountCodes», уже обновленную, но еще не выполненную (что требуется части «NOT EXISTS»), она будет заблокирована. – Quassnoi

1

Я бы рекомендовал строить таблицу моста вместо того, чтобы в EntryId в DiscountCodes таблице с EntryId и DiscountCodeId. Поместите Unique Constraint в оба этих поля.

Таким образом, ваша точка входа столкнется с нарушением ограничения при попытке ввести дубликат.

+0

Зачем нужен стол для моста? Вы также можете создать уникальный индекс на существующем 'discountCodes.entryId' (и на самом деле это должно быть сделано в любом случае). – Quassnoi

+0

@Quassnoi 'DiscountCodes' обновляется, а не вставлен против, потому что таблица предварительно заполнена. Ваше решение не остановит 'DiscountCode.EntryId' от перезаписывания, ака, использовано дважды. – Khan

0

я бы просто положить IDENTITY поле на каждом столе и пусть соответствующая запись соответствует соответствующий discountCode - то есть, если у вас есть тысяча discountCodes фронт, ваш столбец идентификационной информации в таблице discountCodes будет варьироваться от 1 до 1000. Это будет соответствовать вашим первым от 1 до 1000 записей. Если вы получаете более 1000 записей, просто добавьте один скидочный код за дополнительную запись.

Таким образом, SQL Server обрабатывает все проблемные логические «получить следующий номер в последовательности» для вас.

+0

'IDENTITY' не гарантируется последовательным. – Quassnoi

+0

@Quassnoi это интересно, за исключением случая, когда вы должны были удалить запись или объявить свою личность, чтобы она увеличивалась на что-либо, отличное от 1, или каким-либо образом в нее вписывалась, в каком контексте идентификация не могла быть последовательной? – Jason

+0

Если вы не намеренно устанавливаете приращение на что-то отличное от 1 или удаляете строки из таблицы. – dpw

0

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

DECLARE @state Int 

BEGIN TRAN 

-- here we're using 1 sec's as time out, but you should determine what the min value is for your instance 
EXEC @state = sp_getapplock 'SyncIt', 'Exclusive', 'Transaction', 1000 

-- do insert/update/etc... 

-- if you like you can be a little verbose and explicit, otherwise line below shouldn't be needed 
EXEC sp_releaseapplock 'SyncIt', 'Transaction' 

COMMIT TRAN 
Смежные вопросы