在某些情況下我們需要向數據表中更新一條記錄的狀態,然后再把它取出來,但這時如果你在更新前並沒有一個確認惟一記錄的主鍵就沒有辦法知道哪條記錄被更新了。
舉例說明下:
有一個發放新手卡的程序,設計數據庫時常見的方案有兩種:
方案一:使用一張表,新手卡和領取記錄都在一起,這樣主要字段就是新手卡(主鍵)、用戶ID(惟一)、領取狀態(非必要)等
這樣的話數據庫操作就簡單了,直接一條update sql,將用戶id更新到這張表里,然后根據用戶ID再select出來就好了。但這樣記錄很多時就會有效率的問題,暫不討論。
方案二:使用兩張表,一張存放新手卡,另外一張存放領取記錄。新手卡表里面有新手卡(主鍵)、新手卡狀態等字段。
在操作時也可以有兩種方式:
一是先從新手卡表中select出一條記錄,然后去更新它的狀態,再之后插入到領取記錄表中。
但這種方式最大的問題在於高並發情況下,會出現多個用戶select出了同一條記錄,這樣就只能有一個人成功,其他人會失敗。
二是先從新手卡中更新一條記錄,然后取出這條記錄插入到領取記錄表中。由於是先update再select所以很好適應高並發的情況,
但是現在就遇到前面說的問題了:怎樣獲取剛才更新記錄的ID呢?
下面代碼是從stackoverflow上找到的答案,借用一下:
SET @update_id := 0; UPDATE some_table SET row = 'value', id = (SELECT @update_id := id) WHERE some_other_row = 'blah' LIMIT 1; SELECT @update_id;
大致思路就是首先聲明一個用戶變量 @update_id ,之后在update數據時要多更新一個字段,就是將當前主鍵值更新為當前主鍵值(其實就是沒更新),更新主鍵字段並不是目的,只是為了將當前主鍵值賦值給@update_id,就是這句: ( SELECT @update_id := id )。 (個人理解,水平有限可能會有出入)
另外如果更新了多條記錄也可以用下面的方式
SET @uids := null; UPDATE footable SET foo = 'bar' WHERE fooid > 5 AND ( SELECT @uids := CONCAT_WS(',', fooid, @uids) ); SELECT @uids;
注:上面的方法不適用於有HAVING、GROUP BY或者ORDER BY子句中,否則可能出入與預期不同的結果。
參考官方手冊說明 http://dev.mysql.com/doc/refman/5.1/zh/language-structure.html