Могу ли я выбрать и обновить одновременно? - PullRequest
5 голосов
/ 19 февраля 2010

Это слишком упрощенное объяснение того, над чем я работаю.
У меня есть таблица со статусом столбца. Несколько экземпляров приложения извлекают содержимое первой строки со статусом NEW, обновляют статус до WORKING и затем переходят к работе с содержимым.
Это достаточно легко сделать с помощью двух вызовов базы данных; сначала SELECT, затем UPDATE. Но я хочу сделать все это за один вызов, чтобы другой экземпляр приложения не вытягивал ту же строку. Вроде как SELECT_AND_UPDATE вещь.

Является ли хранимая процедура лучшим способом?

Ответы [ 6 ]

8 голосов
/ 19 февраля 2010

Вы можете использовать оператор OUTPUT .

DECLARE @Table TABLE (ID INTEGER, Status VARCHAR(32))
INSERT INTO @Table VALUES (1, 'New')
INSERT INTO @Table VALUES (2, 'New')
INSERT INTO @Table VALUES (3, 'Working')

UPDATE  @Table
SET     Status = 'Working'
OUTPUT  Inserted.*
FROM    @Table t1
        INNER JOIN (
          SELECT  TOP 1 ID 
          FROM    @Table
          WHERE   Status = 'New'
        ) t2 ON t2.ID = t1.ID
2 голосов
/ 19 февраля 2010

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

Если это так, взгляните на ответ, который я предоставил ранее сегодня, который описывает, как реализовать эту логику, используя транзакцию в сочетании с подсказками таблиц UPDLOCK и READPAST: Рядные замки - используя их вручную

Лучше всего завернутый в sproc.

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

1 голос
/ 19 февраля 2010

Да, и, возможно, используйте подсказку блокировки строки, чтобы сохранить ее изолированной от других потоков, например.

UPDATE
Jobs WITH (ROWLOCK, UPDLOCK, READPAST)
SET Status = 'WORKING'
WHERE JobID =
(SELECT Top 1 JobId FROM Jobs WHERE Status = 'NEW')

РЕДАКТИРОВАТЬ: Rowlock будет лучше, как предложено Quassnoi, но та же идея применяется для обновления в одном запросе.

1 голос
/ 19 февраля 2010

Хранимая процедура - это путь. Вам нужно посмотреть на транзакции. Сервер Sql был рожден для такого рода вещей.

1 голос
/ 19 февраля 2010

Вы должны сделать три вещи здесь:

  1. Заблокируйте строку, над которой вы работаете
  2. Убедитесь, что эта и только эта строка заблокирована
  3. Не ждите заблокированных записей: вместо этого пропустите следующие.

Для этого вы просто выдаете:

SELECT  TOP 1 *
FROM    mytable (ROWLOCK, UPDLOCK, READPAST)
WHERE   status = 'NEW'
ORDER BY
        date

UPDATE  …

в рамках транзакции.

1 голос
/ 19 февраля 2010

Не совсем, но вы можете SELECT ... WITH (UPDLOCK), затем UPDATE.. впоследствии.Это так же хорошо, как атомарная операция, поскольку она сообщает базе данных, что вы собираетесь обновить то, что вы ранее выбрали, чтобы она могла блокировать эти строки, предотвращая конфликты с другими клиентами.В Oracle и некоторых других базах данных (я думаю, MySQL) синтаксис: SELECT ... FOR UPDATE.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...