MySQL的SQL语句 - 数据操作语句(8)

- INSERT ... ON DUPLICATE KEY UPDATE 语句

如果指定 ON DUPLICATE KEY UPDATE 子句,并且要插入的行将导致唯一索引或主键中出现重复值,则会更新旧行。例如,如果列 a 声明为 UNIQUE 且包含值 1,则以下两个语句具有类似的效果:

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

(对于一个 InnoDB 表,a 是一个自动递增列,其效果是不同的。对于自动递增列,INSERT 语句会增加自增值,但 UPDATE 不会。)

如果列 b 也是唯一的,则 INSERT 相当于此 UPDATE 语句:

1. 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 ... ON DUPLICATE KEY UPDATE 语句中的 INSERT 部分的列值。换句话说,ON DUPLICATE KEY UPDATE 子句中的 VALUES(col_name) 是指在没有重复键冲突的情况下将插入的 col_name 值。这个函数在多行插入时特别有用。VALUES() 函数仅在 ON DUPLICATE KEY UPDATE 子句或 INSERT 语句中有意义,否则返回 NULL。例如:

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

该语句与以下两个语句相同:

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

注意

从 MySQL 8.0.20 开始,不推荐使用 VALUES() 来引用新行和列,并且在将来的 MySQL 版本中会被删除。相反,请使用行和列别名,如本节后面几段所述。

从 MySQL 8.0.19 开始,可以对行使用别名,也可以选择插入一个或多个列,在 VALUES 或 SET 子句之后,前面加 AS 关键字。使用行别名 new,前面使用 VALUES() 访问新列值的语句可以按如下所示的格式编写:

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

此外,如果使用列别名 m、n 和 p,则可以省略赋值子句中的行别名,语句如下:

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

以这种方式使用列别名时,即使在赋值子句中没有直接使用行别名,也必须在 VALUES 子句后面使用行别名。

如前所述,还可以在 SET 子句中使用行别名和列别名。在前面两个 INSERT ... ON DUPLICATE KEY UPDATE 语句中用 SET 替换 VALUES,可以按如下所示执行:

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

行别名不能与表的名称相同。如果未使用列别名,或者列别名与列名相同,在 ON DUPLICATE KEY UPDATE 子句中则必须使用行别名来区分它们。列别名对于其应用的行别名必须是唯一的(也就是说,引用同一行的列的任何列别名都不能相同)。

对于 INSERT ... SELECT 语句,这些规则适用于 SELECT 查询表达式的可接受形式,可以在 ON DUPLICATE KEY UPDATE 子句中引用这些表达式:

● 对单个表(可能是派生表)上查询的列的引用。

● 对多表连接查询中的列的引用。

● 对 DISTINCT 查询中列的引用。

● 对其他表中的列的引用,只要 SELECT 不使用 GROUP BY。副作用是必须限定对非唯一列名的引用。

不支持对 UNION 中列的引用。要解决此限制,请将 UNION 重写为派生表,以便将其行视为单表结果集。例如,此语句将产生错误:

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

相反,请使用等效语句将 UNION 重写为派生表:

1. INSERT INTO t1 (a, b)
2. SELECT * FROM
3.   (SELECT c, d FROM t2
4.    UNION
5.    SELECT e, f FROM t3) AS dt
6. 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 语句也被标记为不安全。

INSERT DELAYED 语句

1. INSERT DELAYED ...

INSERT 语句的 DELAYED 选项是 MySQL 对标准 SQL 的扩展。在以前版本的 MySQL 中,它可以用于某些类型的表(例如 MyISAM),这样当客户端使用 INSERT DELAYED 时,它会立即从服务器获得一个确认,并且当其他线程不使用该表时,该行将排队等待插入。

MySQL 5.6 中不推荐使用 DELAYED 插入和替换。在 MySQL 8.0 中,不支持 DELAYED。服务器识别但忽略 DELAYED 关键字,将插入作为非延迟插入处理,并生成 ER_WARN_LEGACY_SYNTAX_CONVERTED 警告(“INSERT DELAYED is no longer supported. The statement was converted to INSERT”)。DELAYED 关键字计划在将来的版本中删除。

官方地址:
https://dev.mysql.com/doc/refman/8.0/en/insert-on-duplicate.html
https://dev.mysql.com/doc/refman/8.0/en/insert-delayed.html

MySQL的SQL语句 - 数据操作语句(8)

上一篇:Oracle开启远程访问


下一篇:MySQL的SQL语句 - 数据操作语句(7)