如果您指定了 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 中移除。請改用資料列和欄位別名,如本節接下來的幾個段落所述。
可以使用資料列的別名,並可選擇性地使用要插入的其中一個或多個欄位,在 VALUES
或 SET
子句之後,並在 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;
此外,如果您使用欄位別名 m
、n
和 p
,您可以省略賦值子句中的資料列別名,並將相同的敘述寫成如下形式:
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)