文件首頁
MySQL 9.0 參考手冊
相關文件 下載本手冊
PDF (美式信紙) - 40.0Mb
PDF (A4) - 40.1Mb
Man Pages (TGZ) - 258.2Kb
Man Pages (Zip) - 365.3Kb
Info (Gzip) - 4.0Mb
Info (Zip) - 4.0Mb


MySQL 9.0 參考手冊  /  ...  /  UPDATE 語法

15.2.17 UPDATE 語法

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 BYLIMIT

對於分割的資料表,此陳述式的單一資料表和多個資料表形式都支援將 PARTITION 子句用作資料表參考的一部分。此選項接受一個或多個分割區或子分割區(或兩者)的清單。只會檢查清單中的分割區(或子分割區)是否符合條件,而且不在任何這些分割區或子分割區中的列不會被更新,無論它是否滿足 where_condition

注意

與使用 PARTITIONINSERTREPLACE 陳述式的情況不同,即使列出的分割區(或子分割區)中沒有任何列符合 where_condition,否則有效的 UPDATE ... PARTITION 陳述式也被視為成功。

如需更多資訊和範例,請參閱第 26.5 節「分割區選取」

where_condition 是一個運算式,對於要更新的每個列,其值都為 true。如需運算式語法,請參閱第 11.5 節「運算式」

table_referenceswhere_condition 的指定方式如第 15.2.13 節「SELECT 陳述式」中所述。

您只需要在 UPDATE 中實際更新的欄位中,擁有 UPDATE 權限即可。您只需要對讀取但未修改的任何欄位,擁有 SELECT 權限即可。

UPDATE 陳述式支援下列修飾符

  • 使用 LOW_PRIORITY 修飾符,UPDATE 的執行會延遲到沒有其他用戶端正在從資料表中讀取時才會執行。這只會影響只使用資料表層級鎖定的儲存引擎(例如 MyISAMMEMORYMERGE)。

  • 使用 IGNORE 修飾符,即使更新期間發生錯誤,更新陳述式也不會中止。在唯一鍵值上發生重複鍵衝突的列不會更新。更新為會造成資料轉換錯誤的值的列,會更新為最接近的有效值。如需更多資訊,請參閱「IGNORE 對陳述式執行的影響」

UPDATE IGNORE 陳述式,包括具有 ORDER BY 子句的陳述式,都會被標記為對以陳述式為基礎的複寫不安全。(這是因為列的更新順序會決定哪些列會被忽略。)使用以陳述式為基礎的模式時,這類陳述式會在錯誤日誌中產生警告,並且在使用 MIXED 模式時,會使用以列為基礎的格式寫入二進位日誌。(錯誤 #11758262,錯誤 #50439)如需更多資訊,請參閱第 19.2.1.3 節「決定二進位日誌中安全與不安全的陳述式」

如果您在運算式中存取要更新的資料表中的欄位,UPDATE 會使用該欄位的目前值。例如,以下陳述式會將 col1 設定為比其目前值大一的值

UPDATE t1 SET col1 = col1 + 1;

以下陳述式中的第二個指派會將 col2 設定為目前的(更新的)col1 值,而不是原始的 col1 值。結果是 col1col2 具有相同的值。此行為與標準 SQL 不同。

UPDATE t1 SET col1 = col1 + 1, col2 = col1;

單一資料表 UPDATE 指派通常會從左到右進行評估。對於多個資料表更新,無法保證指派會以任何特定順序執行。

如果您將欄位設定為其目前所擁有的值,MySQL 會注意到這一點,而不會更新它。

如果您透過將欄位設定為 NULL 來更新已宣告為 NOT NULL 的欄位,如果啟用嚴格 SQL 模式,就會發生錯誤;否則,欄位會設定為欄位資料類型的隱含預設值,且警告計數會增加。對於數值類型,隱含預設值為 0,對於字串類型,隱含預設值為空字串 (''),對於日期和時間類型,則為「」值。請參閱第 13.6 節「資料類型預設值」

如果明確更新產生的欄位,唯一允許的值是 DEFAULT。如需產生欄位的相關資訊,請參閱第 15.1.20.8 節「CREATE TABLE 和產生欄位」

UPDATE 會傳回實際變更的列數。mysql_info() C API 函式會傳回符合和更新的列數,以及 UPDATE 期間發生的警告數。

您可以使用 LIMIT row_count 來限制 UPDATE 的範圍。LIMIT 子句是符合列數的限制。只要找到滿足 WHERE 子句的 row_count 列,陳述式就會停止,無論這些列是否實際變更。

如果 UPDATE 陳述式包含 ORDER BY 子句,則會依子句指定的順序更新列。這在某些可能導致錯誤的情況下很有用。假設資料表 t 包含具有唯一索引的欄位 id。以下陳述式可能會因重複鍵錯誤而失敗,具體取決於更新列的順序

UPDATE t SET id = id + 1;

例如,如果資料表在 id 欄位中包含 1 和 2,且 1 在 2 更新為 3 之前更新為 2,就會發生錯誤。為了避免這個問題,請新增 ORDER BY 子句,使具有較大 id 值的列在具有較小值的列之前更新

UPDATE t SET id = id + 1 ORDER BY id DESC;

您也可以執行涵蓋多個資料表的 UPDATE 操作。但是,您無法將 ORDER BYLIMIT 與多個資料表 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 節「外鍵限制」

您無法在子查詢中更新資料表並直接從同一個資料表選取。您可以透過使用多個資料表更新來解決此問題,其中一個資料表是從您實際想要更新的資料表衍生而來,並使用別名來參考衍生的資料表。假設您想要更新名為 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% 或更高,且庫存少於一百項的商品的零售價,您可能會嘗試使用以下 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 合併到最外層查詢區塊中,所以只有在強制將衍生資料表具體化的情況下才會運作。您可以在執行更新之前,將 optimizer_switch 系統變數的 derived_merge 旗標設定為 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 的值。

另一種可能性是重新撰寫子查詢,使其不使用 INEXISTS,如下所示

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;

在這種情況下,子查詢會預設具體化,而不是合併,因此不必停用衍生資料表的合併。