2015-02-08 115 views
0

在包含兩個表users和​​的數據庫中,我想爲用戶分配一個蘋果,然後知道蘋果ID。多個蘋果可以分配給一個用戶。我在PHP工作。如何獲取mysql中更新行的主鍵?

// users asks for an apple - lets find a vacant one 
$apple_id = $dbh->query("SELECT id FROM apple WHERE user_id IS NULL LIMIT 1")->fetchColumn(); 

// hope fully we found an apple, lets assign it 
$sth = $dbh->prepare("UPDATE apple SET user_id = ? WHERE apple_id = ?"); 
$sth->execute(array($user_id,apple_id)); 

問題是,另一個事務可以選擇相同的蘋果,然後一個用戶會覆蓋另一個蘋果。 如果我在更新聲明中包含AND user_id IS NULL,我會冒着無法獲取蘋果的風險。

我知道我可以跟UPDATE apple SET user_id = ? WHERE user_id IS NULL一起去,但後來我不知道蘋果ID。

有沒有辦法獲得更新行的主鍵? 類似lastUpdatedId會很好。

但我認爲我必須使用交易。但是我必須選擇哪個隔離級別?可重複閱讀?可序列化?爲什麼?這有什麼影響?它會只鎖定行還是整個表?

回答

1

你可以做類似如下:

update apple 
    set user_id = if(@apple_id := apple_id, ?, ?) 
    where user_id is null 
    limit 1; 

select @apple_id; 
+0

是啊,那看起來真棒!然而,我剛剛找到了「FOR UPDATE」選擇模式,這似乎是我想要的 – 2015-02-08 14:55:53

+1

@Surrican。 。 。只需小心使用'for update'。來回應用程序在數據庫時間內相當耗時,這會導致鎖持續更長的時間(更不用說,如果您正在等待用戶輸入或有一些有趣的事情發生其他錯誤)。 – 2015-02-08 15:04:35

+0

你能解釋一下你的解決方案嗎?我是否正確地假設你只是使用if語句將當前行的id賦值爲可用,並且兩個'''placehoders都保持相同的值(user_id)?所以if語句實際上只是「誤用」來執行變量賦值? @Gordon Linoff – 2015-02-08 15:10:47

0

SELECT ... FOR UPDATE將產生完全相同的預期效果:

http://dev.mysql.com/doc/refman/5.0/en/innodb-locking-reads.html

$dbh->beginTransaction(); 

// users asks for an apple - lets find a vacant one 
$apple_id = $dbh->query("SELECT id FROM apple WHERE user_id IS NULL LIMIT 1 FOR UPDATE")->fetchColumn(); 

// hope fully we found an apple, lets assign it 
$sth = $dbh->prepare("UPDATE apple SET user_id = ? WHERE apple_id = ?"); 
$sth->execute(array($user_id,apple_id)); 

$dbh->commit(); 

第二個交易將等待第一個承諾。