2009-11-16 4 views
6

Ich habe wirklich Probleme mit einer Abfrage in meiner ColdFusion-Anwendung (MS SQL 2008). Ich erhalte DB Deadlock Fehler bei dieser Transaktion:TSQL SELECT dann UPDATE in einer Transaktion, dann 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, ich habe eine Tabelle (Tabelle 1), die eine große Schlange von sind Elemente darstellt. Benutzer "checken" Gegenstände aus, um ihnen eine Punktzahl zu geben. Nur ein Benutzer kann einen Artikel auf einmal auschecken lassen. Ich muss für einen bestimmten Benutzer einen Block mit 20 Elementen gleichzeitig anfordern. Diese Elemente können nicht bereits ausgecheckt werden, und der Benutzer kann sie nicht schon vorher eingekerbt haben (daher ist die lock_field IS NULL- und NOT EXISTS-Anweisung in SELECT). Sobald ich die Liste von 20 item_ids bestimmt habe, muss ich die Queue-Tabelle aktualisieren, um sie als gesperrt zu markieren, so dass niemand sie gleichzeitig auscheckt. Ich muss auch die Liste der Item_ids zurückgeben.

Ich dachte, es könnte besser funktionieren, wenn ich dies von einer ctransaction zu einem gespeicherten proc auf der SQL-Server-Seite verschoben. Ich bin nur nicht sicher, ob die ctransaction-Sperrung irgendwie eingreift. Ich bin kein TSQL-Guru, also würde etwas Hilfe geschätzt werden.

+0

Haben Sie eine '', um Ihre UPDATE-Operation zu schützen? Auf diese Weise sollten alle Nebenläufigkeitsprobleme verschwinden. – Tomalak

+0

Das Update ist Teil der Transaktion. Ich bin mir nicht sicher, ob ein cflock benötigt wird, oder? –

+0

Eine CFTRANSTRAKTION setzt nur jede SQL-Anweisung zurück, die ausgegeben wird, sobald ein Fehler auftritt. Es sperrt diesen Codeabschnitt nicht. – Tomalak

Antwort

12

Verwenden Sie einen allgemeinen Tabellenausdruck, um die Daten auszuwählen, aktualisieren Sie dann das CTE und geben Sie es aus der UPDATE-Anweisung aus. Auf diese Weise ist alles eine einzige Operation:

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

Danke für die schnelle Antwort. Ich habe gerade realisiert, dass ich alle 3 Felder aus der Select-Abfrage zurückgeben muss, nicht nur die Item_ID. Wie würde das deine Antwort ändern? –

+0

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

+0

Ok, also sollte ich in der Lage sein, diesen Code in einen neuen gespeicherten Proc fallen lassen und natürlich die # varname # ColdFusion-Stil vars zu @vanname gespeicherten proc vars ändern und alle sollten Gut nein? –

2

Nicht viel (sprich: etwas) zu wissen, über PHP, aber einige Erfahrung mit TSQL, Sie können Ihre Anfrage an so etwas zu prüfen, zu ändern:

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 

Dies sollte sicherstellen, dass die ausgewählten Elemente gesperrt werden, bis das Update abgeschlossen ist.

edit: eine output-Klausel hinzugefügt, da die ursprüngliche Frage auch wissen wollte, welche Zeilen aktualisiert wurden.

+0

Ja, aber das Problem mit der Auswahl in das UPDATE ist, dass ich den Inhalt der SELECT-Abfrage als Teil dieser Transaktion zurückgeben muss. –

+0

Warum brauchen Sie HOLDLOCK? Wird das Schloss sowieso nicht gehalten, da du ein UPDATE machst? – erikkallen