MySQL的SQL語句 - 資料操作語句(15)- UPDATE 語句
UPDATE 語句
UPDATE 是修改表中行的 DML 語句。
UPDATE 語句可以用 WITH 子句開頭,定義在 UPDATE 中可訪問的公共表表達式。
單表語法:
1. UPDATE [LOW_PRIORITY] [IGNORE] table_reference 2. SET assignment_list 3. [WHERE where_condition] 4. [ORDER BY ...] 5. [LIMIT row_count] 6. 7. value: 8. {expr | DEFAULT} 9. 10. assignment: 11. col_name = value 12. 13. assignment_list: 14. assignment [, assignment] ...
多表語法:
1. UPDATE [LOW_PRIORITY] [IGNORE] table_references
2. SET assignment_list
3. [WHERE where_condition]
對於單表語法,UPDATE 語句用新值更新命名錶中現有行的列。SET 子句指示要修改的列及其應給定的值。每個值可以用表示式給定,也可以使用關鍵字 DEFAULT 將列顯式設定為其預設值。WHERE 子句(如果給定)指定要更新哪些行。如果沒有 WHERE 子句,所有行都將更新。如果指定了 ORDER BY 子句,則按指定的順序更新行。LIMIT 子句對可以更新的行數進行了限制。
對於多表語法,UPDATE 更新 table_references 中每個表中滿足條件的行。每個匹配的行都會更新一次,即使它與條件匹配多次。對於多表語法,不能使用 ORDER BY 和 LIMIT。
對於分割槽表,此語句的單表和多表形式都支援使用 PARTITION 選項用作表引用的一部分。此選項接受分割槽或子分割槽列表。只檢查列出的分割槽(或子分割槽)是否匹配,不在這些分割槽或子分割槽中的行不會更新,無論它是否滿足 where_condition 條件。
注意
與在 INSERT 或 REPLACE 語句中使用 PARTITION 的情況不同,即使列出的分割槽(或子分割槽)中沒有與 where_condition 匹配的行,UPDATE ... PARTITION 語句也被認為是成功的。
where_condition 是一個表示式,要更新的每一行都必須滿足此表示式的條件。
只需要擁有在 UPDATE 語句實際更新引用的列的 UPDATE 許可權。對於任何已讀取但未修改的列,只需要 SELECT 許可權。
UPDATE 語句支援以下修飾符:
● 使用 LOW_PRIORITY 修飾符,UPDATE 的執行將被延遲,直到沒有其他客戶端從表中讀取資料。這隻影響只使用表級鎖定的儲存引擎(如 MyISAM、MEMORY 和 MERGE)。
● 使用 IGNORE 修飾符,即使在更新過程中發生錯誤,更新語句也不會中止。不會更新在唯一鍵值上引發重複鍵衝突的行。可能導致資料轉換錯誤的值的行將更新為最接近的有效值。
包括 ORDER BY 子句的 UPDATE IGNORE 語句被標記為不安全的基於語句的複製。(這是因為行的更新順序決定了哪些行被忽略。)當使用基於語句的模式時,這些語句在錯誤日誌中生成警告,在使用 MIXED 模式時,這些語句將使用基於行的格式寫入二進位制日誌。
如果從要在表示式中更新的表中訪問列,則 UPDATE 將使用該列的當前值。例如,下面的語句將 col1 設定為比當前值多1:
1. UPDATE t1 SET col1 = col1 + 1;
下面語句中的第二個賦值將 col2 設定為當前(更新的)col1 值,而不是原始 col1 值。結果是 col1 和 col2 的值相同。此行為與標準 SQL 不同。
1. UPDATE t1 SET col1 = col1 + 1, col2 = col1;
單表 UPDATE 分配通常從左到右進行計算。對於多表更新,不能保證以任何特定的順序執行分配。
如果將列設定為當前的值,MySQL 會注意到這一點,並且不會更新它。
如果把已宣告為 NOT NULL 的列設定為 NULL,則在啟用了嚴格 SQL 模式會出錯;否則,該列將設定為列資料型別的隱式預設值,並且警告計數將遞增。對於數值型別,隱式預設值為0;對於字串型別,隱式預設值為空字串(''),對於日期和時間型別,預設值為“零”。
如果顯式更新生成列,則唯一允許的值是 DEFAULT。
UPDATE 返回實際更改的行數。mysql_info() C API 函式返回匹配和更新的行數以及更新過程中出現的警告數。
可以使用 LIMIT row_count 來限制 UPDATE 的範圍。LIMIT 子句是匹配行的限制。只要找到滿足 WHERE 子句的 row_count 行,語句就會立即停止,而不管這些行是否實際被更改。
如果 UPDATE 語句包含 ORDER BY 子句,則按該子句指定的順序更新行。這在某些可能導致錯誤的情況下非常有用。假設表 t 包含一個具有唯一索引的列 id。以下語句可能會出現重複鍵錯誤而失敗,這取決於行的更新順序:
1. UPDATE t SET id = id + 1;
例如,如果表在 id 列中包含值 1 和 2,並且在 2 更新為 3 之前 1 先更新為2,則會發生錯誤。若要避免此問題,請新增 ORDER BY 子句,使 id 值較大的行在值較小的行之前更新:
1. UPDATE t SET id = id + 1 ORDER BY id DESC;
還可以執行覆蓋多個表的 UPDATE 操作。但是,不能將 ORDER BY 或 LIMIT 用於多表更新。table_references 子句列出了連線中涉及的表。
1. UPDATE items,month SET items.price=month.price
2. WHERE items.id=month.id;
前面的示例顯示了使用逗號運算子的內部聯接,但多表更新語句可以使用 SELECT 語句中允許的任何型別的聯接,例如 LEFT JOIN。
如果使用包含 InnoDB 表且有外來鍵約束的多表 UPDATE 語句,那麼 MySQL 優化器可能會按照與父/子關係不同的順序處理表。在本例中,語句失敗並回滾。相反,更新一個表並依賴 InnoDB 提供的 ON UPDATE 功能來相應地修改其他表。
不能在更新一個表的同時直接從子查詢中對同一表進行選擇。可以通過使用多表更新來解決此問題,其中一個表是從實際要更新的表派生的,並使用別名引用派生表。假設希望更新一個名為 items 的表,該表是使用以下語句定義的:
1. CREATE TABLE items (
2. id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
3. wholesale DECIMAL(6,2) NOT NULL DEFAULT 0.00,
4. retail DECIMAL(6,2) NOT NULL DEFAULT 0.00,
5. quantity BIGINT NOT NULL DEFAULT 0
6. );
若要降低利潤為30%或更高並且庫存少於100的商品的零售價,可以嘗試使用如下所示的 UPDATE 語句,該語句在 WHERE 子句中使用子查詢。如下所示,此語句不起作用:
1. mysql> UPDATE items
2. > SET retail = retail * 0.9
3. > WHERE id IN
4. > (SELECT id FROM items
5. > WHERE retail / wholesale >= 1.3 AND quantity > 100);
6. ERROR 1093 (HY000): You can't specify target table 'items' for update in FROM clause
替代方法是可以使用多表更新,其中子查詢被移動到要更新的表列表中,使用別名在最外層的 WHERE 子句中引用它,如下所示:
1. UPDATE items,
2. (SELECT id FROM items
3. WHERE id IN
4. (SELECT id FROM items
5. WHERE retail / wholesale >= 1.3 AND quantity < 100))
6. AS discounted
7. SET items.retail = items.retail * 0.9
8. WHERE items.id = discounted.id;
因為預設情況下,優化器會嘗試將派生表 discounted 合併到最外層的查詢塊中,只有在強制物化派生表時,這才有效。可以在執行更新之前將 optimizer_switch 系統變數的 derived_merge 標誌設定為 off,或使用 NO_MERGE 優化器提示來執行此操作,如下所示:
1. UPDATE /*+ NO_MERGE(discounted) */ items,
2. (SELECT id FROM items
3. WHERE retail / wholesale >= 1.3 AND quantity < 100)
4. AS discounted
5. SET items.retail = items.retail * 0.9
6. WHERE items.id = discounted.id;
在這種情況下使用優化器提示的好處是,它只適用於使用它的查詢塊中,因此在執行 UPDATE 之後,不必再次更改 optimizer_switch 的值。
另一種可能是重寫子查詢,使其不使用 IN 或 EXISTS,如下所示:
1. UPDATE items,
2. (SELECT id, retail / wholesale AS markup, quantity FROM items)
3. AS discounted
4. SET items.retail = items.retail * 0.9
5. WHERE discounted.markup >= 1.3
6. AND discounted.quantity < 100
7. AND items.id = discounted.id;
在這種情況下,子查詢預設情況下是物化的,而不是合併的,因此不需要禁用派生表的合併。
官方網址: