先看示例:
SELECT uin,account,password,create_user_uin_tree FROM sys_user
結果:

表中的create_user_uin_tree標識該條記錄由誰創建。
創建新用戶時,根據當前登錄用戶的uin及新創建的用戶uin,有如下SQL:
select concat(ifNULL(create_user_uin_tree,concat('_',2,'_')),'|_','97',"_") from sys_user where uin=2
結果:

那么修改的create_user_uin_tree的標識SQL為:
update sys_user set create_user_uin_tree=(select concat(ifNULL(temp.create_user_uin_tree,concat('_',2,'_')),'|_','97',"_") from sys_user temp where temp.uin=2) where uin = 97;
報錯信息:
Error Code: 1093. You can't specify target table 'sys_user' for update in FROM clause 0.000 sec
原因:
mysql中不支持子查詢更新,准確的說是更新的表不能在set和where中用於子查詢。那串英文錯誤提示就是說,不能先select出同一表中的某些值,再update這個表(在同一語句中)。
兩種方法:
1、select from 修改為子查詢方式
2、inner join
方法1:子查詢方式
調整了下SQL:
update sys_user set create_user_uin_tree=(select temp.tree from (select concat(ifNULL(create_user_uin_tree,concat('_',97,'_')),'|_','98',"_") as tree from sys_user where uin=97) temp) where uin = 98;
我將作為子集,
select concat(ifNULL(create_user_uin_tree,concat('_',97,'_')),'|_','98',"_") as tree from sys_user where uin=97) temp
然后再
select temp.tree from(子集)
子集,這樣就不會 select 和 update 都是同一個表。致此問題得到完美解決。
方法2:inner join
參考:
在sql server中,我們可是使用以下update語句對表進行更新:
update a set a.xx= (select yy from b) where a.id = b.id ;
但是在mysql中,不能直接使用set select的結果,必須使用inner join:
update a inner join (select yy from b) c on a.id =b.id set a.xx = c.yy
MySQL不允許SELECT FROM后面指向用作UPDATE的表,有時候讓人糾結。當然,有比創建無休止的臨時表更好的辦法。本文解釋如何UPDATE一張表,同時在查詢子句中使用SELECT.
假設我要UPDATE的表跟查詢子句是同一張表,這樣做有許多種原因,例如用統計數據更新表的字段(此時需要用group子句返回統計值),從某一條記錄的字段update另一條記錄,而不必使用非標准的語句,等等。舉個例子:
錯誤提示是:ERROR 1093 (HY000): You can't specify target table 'apples' for update in FROM clause. MySQL手冊UPDATE documentation這下面有說明 : “Currently, you cannot update a table and select from the same table in a subquery.”
在這個例子中,要解決問題也十分簡單,但有時候不得不通過查詢子句來update目標。好在我們有辦法。
既然MySQL是通過臨時表來實現FROM子句里面的嵌套查詢,那么把嵌套查詢裝進另外一個嵌套查詢里,可使FROM子句查詢和保存都是在臨時表里進行,然后間接地在外圍查詢被引用。下面的語句是正確的:
CREATE TABLE `user2` ( `id` INT(11) NOT NULL, `name` VARCHAR(50) DEFAULT NULL, `device` VARCHAR(40) DEFAULT NULL, `memo` VARCHAR(40) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=INNODB DEFAULT CHARSET=utf8; INSERT INTO `user2`(`id`,`name`,`device`,`memo`) VALUES (1,'duan','11',NULL),(2,'liang','12',NULL),(3,'hou','13',NULL);
//子查詢
UPDATE user2 SET memo=(SELECT temp.dc FROM (SELECT device AS dc FROM user2 WHERE id=2) temp) WHERE id = 2;
//列=列
UPDATE user2 SET memo=device;
//join
如果你想了解更多其中的機制,請閱讀MySQL Internals Manual相關章節。
沒有解決的問題
一個常見的問題是,IN()子句優化廢品,被重寫成相關的嵌套查詢,有時(往往?)造成性能低下。把嵌套查詢裝進另外一個嵌套查詢里並不能阻止它重寫成相關嵌套,除非我下狠招。這種情況下,最好用JOIN重構查詢(rewrite such a query as a join)。
另一個沒解決的問題是臨時表被引用多次。“裝進嵌套查詢”的技巧無法解決這些問題,因為它們在編譯時被創建,而上面討論的update問題是在運行時。
另外一個示例:交換兩條記錄中的字段值方法
表test:
id priority
1 1
2 2
方法一:
mysql語句如下:
update question set sort=(case when id=7 then (select a.sort from (select tmp.* from question tmp) a
where a.id=8) when id=8 then (select a.sort from (select tmp.* from question tmp) a where a.id=7) end)
where id=7 or id=8;
方法二:
update question as q1 join question as q2 on (q1.id=7 and q2.id = 8)
or(q1.id = 8 and q2.id=7)
set q1.sort = q2.sort,q2.sort=q1.sort;
