2009-11-16 3 views
6

У меня действительно возникает проблема с запросом в моем приложении ColdFusion (с поддержкой MS SQL 2008). Я получаю ошибку DB тупикового по данной сделке:TSQL SELECT, затем UPDATE в одной транзакции, затем верните SELECT

<code> 
<cftransaction> 
    <cfquery name="selectQuery"> 
     SELECT TOP 20 item_id, field2, field3 
     FROM Table1 
     WHERE subject_id = #subject_ID# AND lock_field IS NULL AND 
      NOT EXISTS (SELECT * FROM Table2 WHERE Table2.user_ID = #user_ID# Table1.item_id = Table2.item_id) 
    </cfquery> 

    <cfquery name="updateQuery"> 
     UPDATE Table1 
     SET lock_field = 1, locked_by = #user_ID# 
     WHERE Table1.item_id IN (#ValueList(selectQuery.item_id#) 
    </cfquery> 
</cftransaction> 
</code> 

Bascially, у меня есть таблица (Table1), который представляет собой большую очередь элементов ожидания. Пользователи «проверяют» предметы, чтобы дать им оценку. Только один пользователь может одновременно проверять элемент. Мне нужно запросить блок из 20 предметов за раз для данного пользователя. Эти элементы уже не могут быть проверены, и пользователь не может их уже забить (до этого в команде SELECT указывалось оператор lock_field IS NULL и NOT EXISTS). Как только я определил список из 20 item_ids, мне нужно затем обновить таблицу очередей, чтобы пометить их как заблокированные, чтобы никто другой не проверял их одновременно. Мне также нужно вернуть этот список item_ids.

Я думал, что это может сработать лучше, если я переместил это из cftransaction в сохраненный proc на стороне SQL Server. Я просто не уверен, что блокировка блокировки каким-то образом вмешивается. Я не гуру TSQL, поэтому некоторые из них будут оценены.

+0

У вас есть '' на месте, чтобы защитить свою операцию UPDATE? Таким образом, все проблемы параллелизма должны исчезнуть. – Tomalak

+0

Обновление является частью cftransaction. Я не уверен, что нужен cflock, нет? –

+0

Cftransaction просто отбрасывает каждый SQL-запрос, выпущенный внутри него, как только возникает ошибка. Он не блокирует этот раздел кода. – Tomalak

ответ

12

Используйте общее выражение таблицы для выбора данных, затем обновите CTE и выведите из инструкции UPDATE. Таким образом, все это одна единственная операция:

with cte as (
SELECT TOP 20 item_id, field2, field3 
FROM Table1 WITH (ROWLOCK, UPDLOCK) 
WHERE subject_id = #subject_ID# 
AND lock_field IS NULL 
AND NOT EXISTS (
    SELECT * FROM Table2 
    WHERE Table2.user_ID = #user_ID# AND Table1.item_id = Table2.item_id)) 
update cte 
SET lock_field = 1, locked_by = #user_ID# 
output inserted.item_id; 
+0

Спасибо за быстрый ответ. Я просто понял, что мне нужно вернуть все 3 поля из запроса select, а не только item_id. Как это изменит ваш ответ? –

+0

OUTPUT inserted.item_id, insert.field2, insert.field3 –

+0

Итак, я должен уметь отбрасывать этот код в новый сохраненный proc и, очевидно, менять vars # varname # ColdFusion vars на @varname хранимые proc vars, и все должно быть хорошо, нет? –

2

Не зная много (читай: ничего) о PHP, но имеющие некоторый опыт работы с TSQL вы можете рассмотреть вопрос об изменении вашего запроса на что-то вроде этого:

update TABLE1 set LOCK_FIELD = 1 
output inserted.item_id, inserted.OtherInterestingColumnsGoHere 
from (select top 20 item_id from TABLE1(holdlock)) as a 
where a.item_id = table1.item_id 

Это должно гарантировать, что элементы, выбранные вами, будут заблокированы до завершения обновления.

Редактировать: добавлено предложение вывода, поскольку исходный вопрос также хотел знать, какие строки были обновлены.

+0

Да, но проблема с помещением выбора внутри UPDATE заключается в том, что мне нужно ВОЗВРАТИТЬ содержимое запроса выбора как часть этой транзакции. –

+0

Зачем вам HOLDLOCK? Не будет ли замок заблокирован, так как вы делаете ОБНОВЛЕНИЕ? – erikkallen