文件首頁
MySQL 9.0 參考手冊
相關文件 下載本手冊
PDF (US Ltr) - 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 參考手冊  /  ...  /  INSERT ... ON DUPLICATE KEY UPDATE 陳述式

15.2.7.2 INSERT ... ON DUPLICATE KEY UPDATE 陳述式

如果您指定了 ON DUPLICATE KEY UPDATE 子句,且要插入的資料列會導致 UNIQUE 索引或 PRIMARY KEY 中出現重複的值,則會執行舊資料列的 UPDATE 操作。例如,如果欄位 a 被宣告為 UNIQUE 且包含值 1,則以下兩個敘述具有類似的效果:

INSERT INTO t1 (a,b,c) VALUES (1,2,3)
  ON DUPLICATE KEY UPDATE c=c+1;

UPDATE t1 SET c=c+1 WHERE a=1;

效果並非完全相同:對於 InnoDB 資料表,其中 a 是自動遞增欄位,INSERT 敘述會增加自動遞增值,但 UPDATE 則不會。

如果欄位 b 也是唯一的,則 INSERT 相當於以下的 UPDATE 敘述:

UPDATE t1 SET c=c+1 WHERE a=1 OR b=2 LIMIT 1;

如果 a=1 OR b=2 符合多個資料列,則只會更新 一個 資料列。通常,您應該避免在具有多個唯一索引的資料表上使用 ON DUPLICATE KEY UPDATE 子句。

使用 ON DUPLICATE KEY UPDATE 時,每個資料列的受影響資料列值,如果該資料列是作為新資料列插入,則為 1;如果現有資料列被更新,則為 2;如果現有資料列被設定為其目前的值,則為 0。如果您在連線到 mysqld 時,將 CLIENT_FOUND_ROWS 旗標指定給 mysql_real_connect() C API 函式,則如果現有資料列被設定為其目前的值,受影響的資料列值為 1(而不是 0)。

如果資料表包含 AUTO_INCREMENT 欄位,且 INSERT ... ON DUPLICATE KEY UPDATE 插入或更新資料列,則 LAST_INSERT_ID() 函式會傳回 AUTO_INCREMENT 值。

ON DUPLICATE KEY UPDATE 子句可以包含多個欄位賦值,並以逗號分隔。

ON DUPLICATE KEY UPDATE 子句中的賦值運算式中,您可以使用 VALUES(col_name) 函式來參考 INSERT 敘述中 INSERT ... ON DUPLICATE KEY UPDATE 敘述的欄位值。換句話說,ON DUPLICATE KEY UPDATE 子句中的 VALUES(col_name) 參考的是 col_name 的值,如果沒有發生重複鍵衝突,該值將被插入。此函式在多列插入中特別有用。VALUES() 函式僅在 ON DUPLICATE KEY UPDATE 子句或 INSERT 敘述中有意義,否則會傳回 NULL。範例:

INSERT INTO t1 (a,b,c) VALUES (1,2,3),(4,5,6)
  ON DUPLICATE KEY UPDATE c=VALUES(a)+VALUES(b);

該敘述與以下兩個敘述相同:

INSERT INTO t1 (a,b,c) VALUES (1,2,3)
  ON DUPLICATE KEY UPDATE c=3;
INSERT INTO t1 (a,b,c) VALUES (4,5,6)
  ON DUPLICATE KEY UPDATE c=9;
注意:

使用 VALUES() 參考新資料列和欄位已被棄用,並可能在未來版本的 MySQL 中移除。請改用資料列和欄位別名,如本節接下來的幾個段落所述。

可以使用資料列的別名,並可選擇性地使用要插入的其中一個或多個欄位,在 VALUESSET 子句之後,並在 AS 關鍵字之前。使用資料列別名 new,先前使用 VALUES() 來存取新欄位值的敘述可以寫成如下所示的形式:

INSERT INTO t1 (a,b,c) VALUES (1,2,3),(4,5,6) AS new
  ON DUPLICATE KEY UPDATE c = new.a+new.b;

此外,如果您使用欄位別名 mnp,您可以省略賦值子句中的資料列別名,並將相同的敘述寫成如下形式:

INSERT INTO t1 (a,b,c) VALUES (1,2,3),(4,5,6) AS new(m,n,p)
  ON DUPLICATE KEY UPDATE c = m+n;

以這種方式使用欄位別名時,您仍然必須在 VALUES 子句之後使用資料列別名,即使您在賦值子句中沒有直接使用它。

像這樣在 UPDATE 子句中使用 VALUES()INSERT ... SELECT ... ON DUPLICATE KEY UPDATE 敘述會拋出警告:

INSERT INTO t1
  SELECT c, c+d FROM t2
  ON DUPLICATE KEY UPDATE b = VALUES(b);

您可以改用子查詢來消除此類警告,如下所示:

INSERT INTO t1
  SELECT * FROM (SELECT c, c+d AS e FROM t2) AS dt
  ON DUPLICATE KEY UPDATE b = e;

您也可以像先前所述,在 SET 子句中使用資料列和欄位別名。將剛剛顯示的兩個 INSERT ... ON DUPLICATE KEY UPDATE 敘述中用 SET 代替 VALUES,可以如下所示進行:

INSERT INTO t1 SET a=1,b=2,c=3 AS new
  ON DUPLICATE KEY UPDATE c = new.a+new.b;

INSERT INTO t1 SET a=1,b=2,c=3 AS new(m,n,p)
  ON DUPLICATE KEY UPDATE c = m+n;

資料列別名不得與資料表的名稱相同。如果未使用欄位別名,或者如果它們與欄位名稱相同,則必須在 ON DUPLICATE KEY UPDATE 子句中使用資料列別名來區分它們。欄位別名必須相對於它們所套用的資料列別名是唯一的(也就是說,沒有參考相同資料列欄位的欄位別名可以是相同的)。

對於 INSERT ... SELECT 敘述,以下規則適用於您可以在 ON DUPLICATE KEY UPDATE 子句中參考的 SELECT 查詢運算式的可接受形式:

  • 從單一資料表(可能是衍生資料表)的查詢中參考欄位。

  • 從多個資料表上的聯結查詢中參考欄位。

  • DISTINCT 查詢中參考欄位。

  • 參考其他資料表中的欄位,前提是 SELECT 沒有使用 GROUP BY。一個副作用是,您必須限定對非唯一欄位名稱的參考。

不支援從 UNION 參考欄位。要解決此限制,請將 UNION 重寫為衍生資料表,以便將其資料列視為單一資料表的結果集。例如,此敘述會產生錯誤:

INSERT INTO t1 (a, b)
  SELECT c, d FROM t2
  UNION
  SELECT e, f FROM t3
ON DUPLICATE KEY UPDATE b = b + c;

請改用一個等效的敘述,將 UNION 重寫為衍生資料表:

INSERT INTO t1 (a, b)
SELECT * FROM
  (SELECT c, d FROM t2
   UNION
   SELECT e, f FROM t3) AS dt
ON DUPLICATE KEY UPDATE b = b + c;

將查詢重寫為衍生資料表的技術,也允許參考 GROUP BY 查詢中的欄位。

由於 INSERT ... SELECT 敘述的結果取決於 SELECT 中資料列的順序,而此順序並不總是能保證,因此在記錄來源和複本的 INSERT ... SELECT ON DUPLICATE KEY UPDATE 敘述時,有可能會出現差異。因此,INSERT ... SELECT ON DUPLICATE KEY UPDATE 敘述被標記為對基於敘述的複製不安全。當使用基於敘述的模式時,此類敘述會在錯誤日誌中產生警告,並且在使用 MIXED 模式時,會使用基於資料列的格式寫入二進制日誌。針對具有多個唯一鍵或主鍵的資料表執行的 INSERT ... ON DUPLICATE KEY UPDATE 敘述也會被標記為不安全。(錯誤 #11765650、錯誤 #58637)

另請參閱第 19.2.1.1 節,「基於敘述和基於資料列的複製的優點與缺點」