2009-01-30 13 views
34

Basit bir ölçüt temelinde bir dizi satırı güncellemek ve değiştirilen PK'lerin listesini almak istiyorum. O oluşabilir herhangi eşzamanlılık sorunları vardır bir işlemde sarılı iseSatırları aynı anda SEÇ ve UPDATE geçirmenin bir yolu var mı?

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

: Ben sadece böyle bir şey yapmak ama olası eşzamanlılık sorunları hakkında endişeliyim düşündü? Yoksa bunu yapmanın daha iyi bir yolu var mı?

cevap

65

OUTPUT clause capability of UPDATE (ayrıca DELETE ve INSERT) 'a bakmayı düşünün. bağlantılı MSDN sayfasından Örnek: Bu işlem içinde ise

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

UP GÜNCELLEME SETİ ... FROM ... WHERE .... için geçerlidir? UPDLOCK için – Kiquenet

8

iyisi, önce UPDATE yapmak ve ardından 'sokulmuş GELEN kimliği SEÇ' çalıştırmak için daha kolay olurdu.

fazla bilgi ve örnekler için SQL Tips bir göz atın.

0

, veritabanı kilitleme sistemi eşzamanlılık sorunları ilgilenir. tabii ki, birini kullanmak if (mssql Öntanımlısı kilit kullanır yani, yani bunun geçersiz kılmaz eğer devletler)

0

Edit: benim kötü, Güncellemeden sonra sonuçları göstermek için seçme istedik güncellemediğinden bir seçimden.

Bir alt seçeneğini denediniz mi? Bu işlemek için

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

bir yolu işlemde bunu ve SELECT sorgusu işlem tamamlanana kadar seçili satırların üzerinde bir güncelleme kilidi almak yapmaktır.

BEGIN TRAN 

SELECT Id FROM Table1 WITH (UPDLOCK) 
WHERE AlertDate IS NULL; 

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

COMMIT TRAN 

Bu eşzamanlı istemci SELECT ve UPDATE arasındaki anda seçili satırları günceller ihtimalini ortadan kaldırır.

Hareketi tamamlamak

, güncelleme kilitler açıklanacak.

bu işlemek için başka bir yol İÇİN GÜNCELLEME seçeneğiyle SEÇ için bir imleç bildirmektir. Daha sonra CURSOR GÜNÜNDE GÜNCELLEME. Aşağıdaki sınanmış, ancak aşağıdaki temel fikir vermelidir:

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, bu soruna doğru bir çözüm ve kısa işlemlerde çıkmaza neden olmaz –

+0

Bu sonuç, tüm satırlarda aynı tarih saat değerini elde eder (tek bir SQL çağrısında yaptığınız gibi)? Değilse, döngüden önceki süreyi bir değişkene almalı ve sadece AltertDate'i değişkene ayarlamalısınız. Sorunsa lütfen bunu UPDLOCK için – Thorsten

+1

+1 ve CURSOR için -1 olarak düzenleyin. – jsuddsjr

3

Belki de bundan daha fazlası? SQL 2008'de

declare @UpdateTime datetime 

set @UpdateTime = getutcdate() 

update Table1 set AlertDate = @UpdateTime where AlertDate is null 

select ID from Table1 where AlertDate = @UpdateTime 
+1

Bu, bir işlem içinde bunu yapmasanız bile, başka bir işlemin sonuçlanabilmesi için * çok * zor olacağının bir avantajıdır. –

6

Yıllar sonra ... ÇIKIŞ maddesini kullanmanın

kabul cevap iyidir.

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

14 Eylül 2015 EKLENDİ: Öyle işte burada, fiili sözdizimi kazıp zorunda "? Ben bunun yerine bir tablo değişkeni bir skaler değişken kullanabilir miyim"

Biri sorabilir ... Üzgünüm, ama yapamazsın. Tek bir kimliğe ihtiyacınız varsa SELECT @SomeID = ID from @UpdatedIDs'a sahip olmanız gerekir.

1

Aynı konuyla karşılaştım; Kredi miktarını güncellemeliyim ve DB'den kredi bilgileriyle birlikte değiştirilmiş zaman almam gerekiyor. Bu temelde

eşzamanlı/atomik ben birçok seçenek çalıştı ve sorunumu çözdü birini bulduk (GÜNCELLEME sonra GET) MYSQL

içinde gerçekleştirin.

1) UPDATE

Bunun güncelleme (GÜNCELLENECEK için GET SYNC kadar kilit sürdürüldüğünü) İÇİN OPTION_1 SEÇ, ama GET kadar güncellemeden sonra kilitlemek gerekir.

2) OPTION_2 Saklı yordam

Saklı yordam Redis lua gibi senkronize bir şekilde çalışmaya olmayacak yüzden de biz bu gerçekleştirmek için senkronizasyon kodu vardır gerekir.

3) OPTION_3 İşlem

aşağıda gibi JPA EntityManager kullandık

, daha önce hiç kimsenin güncelleyebilir işlemek ve daha önce i() DB'den modifiye saatle birlikte güncellenen nesneyi alacak taahhüt düşünülmektedir. Ama en son nesneyi almadım. Sadece en son şeyi aldım.

try { 
     entityManager.getTransaction().begin(); 
     //entityManager.persist(object); 
     int upsert = entityManager.createNativeQuery(
     "update com.bill.Credit c set c.balance = c.balance - ?1 
      where c.accountId = ?2 and c.balance >= ?1").executeUpdate(); 
      //c.balance >= ? for limit check 
     Credit newCredit = entityManager.find(Credit.class, "id"); 
     entityManager.refresh(newCredit); //SHOULD GET LATEST BUT NOT 
     entityManager.getTransaction().commit(); 
    } finally {  
     entityManager.unwrap(Session.class).close(); 
    } 

4) OPTION_4 LOCK sorunu çözdü, bu yüzden güncellemeden önce kilidi aldım; GET'ten sonra kilidi serbest bıraktım.

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 ***? Soru "SQL SUNUCU" – Kiquenet

+0

OHH MY BAD için etiketlendi, Cevap MySQL için –