UPDATE
是一個 DML 陳述式,用於修改表格中的列。
UPDATE
陳述式可以以 WITH
子句開始,以定義可在 UPDATE
中存取的通用表格運算式。請參閱 章節 15.2.20,「WITH (通用表格運算式)」。
單表格語法
UPDATE [LOW_PRIORITY] [IGNORE] table_reference
SET assignment_list
[WHERE where_condition]
[ORDER BY ...]
[LIMIT row_count]
value:
{expr | DEFAULT}
assignment:
col_name = value
assignment_list:
assignment [, assignment] ...
多表格語法
UPDATE [LOW_PRIORITY] [IGNORE] table_references
SET assignment_list
[WHERE where_condition]
對於單表格語法,UPDATE
陳述式會使用新值更新指定表格中現有列的欄位。SET
子句表示要修改哪些欄位,以及應給予的值。每個值都可以作為運算式給予,或者使用關鍵字 DEFAULT
來將欄位明確設定為其預設值。WHERE
子句 (如果有的話) 會指定識別要更新哪些列的條件。如果沒有 WHERE
子句,則會更新所有列。如果指定了 ORDER BY
子句,則會按照指定的順序更新列。LIMIT
子句會限制可以更新的列數。
對於多表格語法,UPDATE
會更新 table_references
中指定的每個表格中符合條件的列。即使符合多次條件,每個符合的列也只會更新一次。對於多表格語法,無法使用 ORDER BY
和 LIMIT
。
對於分割表格,此陳述式的單表格和多表格形式都支援使用 PARTITION
子句作為表格參照的一部分。此選項會使用一個或多個分割區或子分割區 (或兩者) 的清單。只會檢查列出的分割區 (或子分割區) 是否符合,並且不會更新不在任何這些分割區或子分割區中的列,無論其是否符合 where_condition
。
與使用 PARTITION
與 INSERT
或 REPLACE
陳述式的情況不同,即使列出的分割區 (或子分割區) 中沒有任何列符合 where_condition
,否則有效的 UPDATE ... PARTITION
陳述式也會被視為成功。
如需更多資訊和範例,請參閱 章節 26.5,「分割區選取」。
where_condition
是一個運算式,對於每個要更新的列,其結果為 true。如需運算式語法,請參閱 章節 11.5,「運算式」。
如同 章節 15.2.13,「SELECT 陳述式」中所述,指定 table_references
和 where_condition
。
您只需要在 UPDATE
中實際更新的欄位中參考的欄位具有 UPDATE
權限。您只需要對讀取但不修改的任何欄位具有 SELECT
權限。
UPDATE
陳述式支援下列修飾符
使用
LOW_PRIORITY
修飾符,UPDATE
的執行會延遲到沒有其他用戶端正在從表格讀取資料時才會執行。這只會影響僅使用表格層級鎖定的儲存引擎 (例如MyISAM
、MEMORY
和MERGE
)。使用
IGNORE
修飾符,即使在更新期間發生錯誤,更新陳述式也不會中止。在唯一索引鍵值上發生重複索引鍵衝突的列不會更新。更新為會導致資料轉換錯誤的值的列會更新為最接近的有效值。如需更多資訊,請參閱 IGNORE 對陳述式執行的影響。
UPDATE IGNORE
陳述式,包括具有 ORDER BY
子句的陳述式,都會被標示為基於陳述式複寫的不安全陳述式。(這是因為列的更新順序會決定哪些列會被忽略。)當使用基於陳述式的模式時,此類陳述式會在錯誤日誌中產生警告,當使用 MIXED
模式時,會使用基於列的格式寫入二進位日誌。(錯誤 #11758262,錯誤 #50439) 如需更多資訊,請參閱 章節 19.2.1.3,「二進位日誌中安全和不安全陳述式的判斷」。
如果您在運算式中存取要更新之表格中的欄位,UPDATE
會使用該欄位的目前值。例如,下列陳述式會將 col1
設定為比目前值大一
UPDATE t1 SET col1 = col1 + 1;
下列陳述式中的第二個指派會將 col2
設定為目前的 (已更新的) col1
值,而不是原始的 col1
值。結果是 col1
和 col2
具有相同的值。此行為與標準 SQL 不同。
UPDATE t1 SET col1 = col1 + 1, col2 = col1;
單表格 UPDATE
指派通常從左至右評估。對於多表格更新,無法保證指派會以任何特定順序執行。
如果您將欄位設定為它目前具有的值,MySQL 會注意到這一點,並且不會更新它。
如果您透過將 NOT NULL
宣告的欄位設定為 NULL
,如果啟用嚴格 SQL 模式,則會發生錯誤;否則,欄位會設定為欄位資料類型的隱含預設值,並且警告計數會遞增。隱含的預設值對於數值類型為 0
、對於字串類型為空字串 (''
),對於日期和時間類型為 “零” 值。請參閱 章節 13.6,「資料類型預設值」。
如果明確更新產生的欄位,則唯一允許的值為 DEFAULT
。如需有關產生欄位的資訊,請參閱 章節 15.1.20.8,「CREATE TABLE 和產生欄位」。
UPDATE
會傳回實際變更的列數。mysql_info()
C API 函式會傳回符合和更新的列數,以及 UPDATE
期間發生的警告數。
您可以使用 LIMIT
來限制 row_count
UPDATE
的範圍。LIMIT
子句是一個符合的列限制。一旦它找到 row_count
個滿足 WHERE
子句的列,無論它們是否實際上已變更,該陳述式就會停止。
如果 UPDATE
陳述式包含 ORDER BY
子句,則會按照該子句指定的順序更新列。這在某些可能會導致錯誤的情況下很有用。假設表格 t
包含一個具有唯一索引的欄位 id
。下列陳述式可能會因為重複索引鍵錯誤而失敗,具體取決於更新列的順序
UPDATE t SET id = id + 1;
例如,如果表格的 id
欄位中包含 1 和 2,並且在將 2 更新為 3 之前將 1 更新為 2,則會發生錯誤。若要避免此問題,請新增 ORDER BY
子句,以使具有較大 id
值的列在具有較小值的列之前更新
UPDATE t SET id = id + 1 ORDER BY id DESC;
您也可以執行涵蓋多個表格的 UPDATE
操作。但是,您無法將 ORDER BY
或 LIMIT
與多表格 UPDATE
一起使用。table_references
子句會列出聯結中涉及的表格。其語法在 章節 15.2.13.2,「JOIN 子句」中說明。以下是一個範例
UPDATE items,month SET items.price=month.price
WHERE items.id=month.id;
先前的範例顯示使用逗號運算子的內部聯結,但是多表格 UPDATE
陳述式可以使用 SELECT
陳述式中允許的任何聯結類型,例如 LEFT JOIN
。
如果您使用涉及 InnoDB
表格的多表格 UPDATE
陳述式,且這些表格具有外部索引鍵限制,則 MySQL 優化工具可能會以與其父/子關係不同的順序處理表格。在這種情況下,陳述式會失敗並回滾。相反,請更新單個表格,並依靠 InnoDB
提供的 ON UPDATE
功能來使其他表格相應修改。請參閱 章節 15.1.20.5,「FOREIGN KEY 限制」。
您無法在子查詢中更新表格並直接從同一個表格選取資料。您可以使用多表格更新來解決此問題,其中一個表格衍生自您實際要更新的表格,並使用別名參考衍生表格。假設您想要更新名為 items
的表格,該表格是使用此處顯示的陳述式定義的
CREATE TABLE items (
id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
wholesale DECIMAL(6,2) NOT NULL DEFAULT 0.00,
retail DECIMAL(6,2) NOT NULL DEFAULT 0.00,
quantity BIGINT NOT NULL DEFAULT 0
);
為了降低任何商品零售價格,若該商品加價幅度為 30% 或更高,且庫存少於 100 件,您可以嘗試使用如下的 UPDATE
陳述式,它在 WHERE
子句中使用子查詢。如下所示,此陳述式無法運作。
mysql> UPDATE items
> SET retail = retail * 0.9
> WHERE id IN
> (SELECT id FROM items
> WHERE retail / wholesale >= 1.3 AND quantity > 100);
ERROR 1093 (HY000): You can't specify target table 'items' for update in FROM clause
您可以改用多表格更新,將子查詢移至要更新的表格列表中,並使用別名在最外層的 WHERE
子句中引用它,如下所示:
UPDATE items,
(SELECT id FROM items
WHERE id IN
(SELECT id FROM items
WHERE retail / wholesale >= 1.3 AND quantity < 100))
AS discounted
SET items.retail = items.retail * 0.9
WHERE items.id = discounted.id;
由於最佳化工具預設會嘗試將衍生表格 discounted
合併到最外層的查詢區塊中,因此只有在您強制將衍生表格實體化時,此方法才有效。您可以在執行更新之前,將 derived_merge
標記(位於 optimizer_switch
系統變數中)設定為 off
,或使用 NO_MERGE
最佳化工具提示,如下所示:
UPDATE /*+ NO_MERGE(discounted) */ items,
(SELECT id FROM items
WHERE retail / wholesale >= 1.3 AND quantity < 100)
AS discounted
SET items.retail = items.retail * 0.9
WHERE items.id = discounted.id;
在這種情況下,使用最佳化工具提示的優點是,它僅在使用的查詢區塊內生效,因此無需在執行 UPDATE
後再次變更 optimizer_switch
的值。
另一種可能性是重新撰寫子查詢,使其不使用 IN
或 EXISTS
,如下所示:
UPDATE items,
(SELECT id, retail / wholesale AS markup, quantity FROM items)
AS discounted
SET items.retail = items.retail * 0.9
WHERE discounted.markup >= 1.3
AND discounted.quantity < 100
AND items.id = discounted.id;
在這種情況下,子查詢預設會被實體化,而不是被合併,因此不需要停用衍生表格的合併。