2009-01-30 12 views
34

Ich möchte eine Reihe von Zeilen basierend auf einem einfachen Kriterium aktualisieren und erhalten die Liste der PKs, die geändert wurden. Ich dachte, ich nur so etwas tun könnte, aber ich bin besorgt über mögliche Parallelität Probleme:Gibt es eine Möglichkeit, Zeilen gleichzeitig auszuwählen und zu aktualisieren?

SELECT Id FROM Table1 WHERE AlertDate IS NULL; 
UPDATE Table1 SET AlertDate = getutcdate() WHERE AlertDate IS NULL; 

Wenn das in einer Transaktion gewickelt sind es Concurrency Probleme, die auftreten können? Oder gibt es einen besseren Weg, dies zu tun?

Antwort

65

Betrachten Sie die OUTPUT clause capability of UPDATE (auch DELETE und INSERT). Beispiel aus der verknüpften MSDN-Seite:

UPDATE TOP (10) HumanResources.Employee 
SET VacationHours = VacationHours * 1.25, 
    ModifiedDate = GETDATE() 
OUTPUT inserted.BusinessEntityID, 
     deleted.VacationHours, 
     inserted.VacationHours, 
     inserted.ModifiedDate 
INTO @MyTableVar; 
+0

Gilt für 'UPDATE SET ... VON ... WHERE ....'? – Kiquenet

8

Es wäre einfacher, zuerst Ihr UPDATE zu machen und dann 'SELECT ID FROM INSERTED' auszuführen.

Werfen Sie einen Blick auf SQL Tips für weitere Informationen und Beispiele.

0

, wenn es innerhalb der Transaktion ist, wird die Datenbank Schließanlage kümmern Concurrency Probleme. natürlich, wenn Sie eine verwenden (die Mssql-Standard ist, dass es Sperre verwendet, so heißt es, wenn Sie das nicht überschreiben)

0

Edit: mein schlechtes, Sie wollten die Auswahl nach dem Update Ergebnisse anzeigen, nicht aktualisieren von einer Auswahl.

Haben Sie versucht, eine Sub-Auswahl?

update mytable set mydate = sysdate 
where mydate in (select mydate from mytable where mydate is null); 
12

Eine Möglichkeit, dies zu umgehen ist es in einer Transaktion zu tun, und machen Sie Ihre SELECT-Abfrage nehmen Sie eine Update-Sperre auf den ausgewählten Zeilen, bis die Transaktion abgeschlossen ist.

Dadurch wird verhindert, dass ein gleichzeitiger Client die Zeilen aktualisiert, die im Moment zwischen SELECT und UPDATE ausgewählt wurden.

Wenn Sie die Transaktion festschreiben, werden die Update-Sperren freigegeben.

Eine andere Möglichkeit, dies zu behandeln, besteht darin, mit der Option FOR UPDATE einen Cursor für SELECT zu deklarieren. Dann UPDATE WHERE CURSOR. Folgendes wird nicht getestet, sollte Ihnen aber die Grundidee geben:

DECLARE cur1 CURSOR FOR 
    SELECT AlertDate FROM Table1 
    WHERE AlertDate IS NULL 
    FOR UPDATE; 

DECLARE @UpdateTime DATETIME 

SET @UpdateTime = GETUTCDATE() 

OPEN cur1; 

FETCH NEXT FROM cur1; 

WHILE @@FETCH_STATUS = 0 
BEGIN 

    UPDATE Table1 AlertDate = @UpdateTime 
    WHERE CURRENT OF cur1; 

    FETCH NEXT FROM cur1; 

END 
+3

+1 für UPDLOCK, es ist eine richtige Lösung für dieses Problem und in kurzen Transaktionen führt nicht zu Deadlocks –

+0

Wird dies dazu führen, dass alle Zeilen denselben Datetime-Wert erhalten (wie in einem einzigen SQL-Aufruf)? Wenn nicht, sollten Sie die Zeit vor der Schleife in eine Variable bringen und nur AltertDate auf die Variable setzen. Wenn es ein Problem ist, bearbeiten Sie das bitte unter – Thorsten

+1

+1 für UPDLOCK und -1 für CURSOR. – jsuddsjr

3

Vielleicht etwas mehr so?

declare @UpdateTime datetime 

set @UpdateTime = getutcdate() 

update Table1 set AlertDate = @UpdateTime where AlertDate is null 

select ID from Table1 where AlertDate = @UpdateTime 
+1

Dies hat den schönen Vorteil, dass es auch dann, wenn Sie dies nicht innerhalb einer Transaktion tun, * sehr * schwierig ist, dass Sie Ergebnisse erhalten, wenn ein anderer Prozess geändert wird. –

0

in SQL 2008 eine neue TSQL-Anweisung „merge“ eingeführt wird, das Einfügen ausführt, aktualisieren oder löschen Operationen auf einer Zieltabelle auf der Grundlage der Ergebnisse eines mit einer Quelltabelle verbinden. Sie können zwei Tabellen synchronisieren, indem Sie Zeilen in einer Tabelle basierend auf den in der anderen Tabelle gefundenen Differenzen einfügen, aktualisieren oder löschen.

http://blogs.msdn.com/ajaiman/archive/2008/06/25/tsql-merge-statement-sql-2008.aspx http://msdn.microsoft.com/en-us/library/bb510625.aspx

6

Viele Jahre später ...

Die akzeptierte Antwort der OUTPUT-Klausel zu verwenden, ist gut.Ich musste die tatsächliche Syntax raben, so ist es hier:

DECLARE @UpdatedIDs table (ID int) 
UPDATE 
    Table1 
SET 
    AlertDate = getutcdate() 
OUTPUT 
    inserted.Id 
INTO 
    @UpdatedIDs 
WHERE 
    AlertDate IS NULL; 

ADDED 14. September 2015: "Kann ich eine skalare Variable anstelle einer Tabelle Variable"

man darf fragen ... Sorry, aber nein, kannst du nicht. Sie müssen SELECT @SomeID = ID from @UpdatedIDs, wenn Sie eine einzelne ID benötigen.

1

Ich habe das gleiche Problem konfrontiert; Ich muss den Kreditbetrag aktualisieren, und muss modifizierte Zeit, zusammen mit Kreditdetails von DB bekommen. Es ist im Grunde

SYNCHRON/atomar durchführen (UPDATE dann GET) in MYSQL

ich viele Möglichkeiten ausprobiert haben und fand eine, die mein Problem gelöst.

1) option_1 SELECT FOR UPDATE

Dies ist das Schloss bis Update (SYNC von GET Aufrechterhaltung UPDATE), aber ich brauche, bis die GET nach der Aktualisierung sperren.

2) OPTION_2 Stored Procedure

gespeicherte Prozedur nicht synchron wie redis lua ausführen, also dort müssen wir auch Sync-Code, dass auszuführen.

3) OPTION_3 Transaktion

I JPA EntityManager wie unten verwendet habe, dachte, dass vor niemandem aktualisieren begehen kann, und bevor begehen i das aktualisierte Objekt erhalten wird zusammen mit modifizierten Zeit (von DB). Aber ich habe das neueste Objekt nicht bekommen. Nur commit ich habe das Neueste.

4) OPTION_4 LOCK hat das Problem gelöst, also habe ich vor dem Update das Schloss erworben; dann nach GET habe ich das Schloss freigegeben.

private Object getLock(final EntityManager entityManager, final String Id){ 

    entityManager.getTransaction().begin(); 
    Object obj_acquire = entityManager.createNativeQuery("SELECT GET_LOCK('" + Id + "', 10)").getSingleResult(); 
    entityManager.getTransaction().commit(); 
    return obj_acquire; 
} 


private Object releaseLock(final EntityManager entityManager, final String Id){ 

    entityManager.getTransaction().begin(); 
    Object obj_release = entityManager.createNativeQuery("SELECT RELEASE_LOCK('" + Id + "')").getSingleResult(); 
    entityManager.getTransaction().commit(); 
    return obj_release; 
} 
+0

*** MySQL ***? Die Frage ist markiert für 'SQL SERVER' – Kiquenet

+0

OHH MY BAD, Antwort ist für MySQL –

Verwandte Themen