1. 程式人生 > 資料庫 >MySQL的SQL語句 - 資料操作語句(15)- UPDATE 語句

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;

在這種情況下,子查詢預設情況下是物化的,而不是合併的,因此不需要禁用派生表的合併。

官方網址: