“MySQL 语句:数据处理语句(DML)”的版本间差异
第455行: | 第455行: | ||
=== INSERT DELAYED === | === INSERT DELAYED === | ||
<syntaxhighlight lang="mysql"> | <syntaxhighlight lang="mysql"> | ||
INSERT DELAYED ... | |||
</syntaxhighlight> | |||
INSERT语句的“'''DELAYED'''”选项是标准 SQL 的 MySQL 扩展。在 MySQL 的早期版本中,它可以用于某些类型的表(例如 MyISAM),这样,当 Client 端使用“INSERT DELAYED”时,它会立即从服务器上获得成功,并且该行排队 await 在 table 插入时插入未被其他任何线程使用。 | |||
* DELAYED 插入和替换在 MySQL 5.6 中已弃用。在 MySQL 5.7 中,不支持DELAYED。服务器识别但忽略DELAYED关键字,将插入作为非延迟插入进行处理,并生成“ER_WARN_LEGACY_SYNTAX_CONVERTED”警告(“不再支持 INSERT DELAYED.该语句已转换为 INSERT”)。 DELAYED关键字计划在将来的版本中删除。 | |||
== LOAD DATA 语句 == | == LOAD DATA 语句 == |
2021年4月12日 (一) 18:19的版本
关于
CALL 语句
CALL sp_name([parameter[,...]])
CALL sp_name[()]
- CALL语句调用先前用“CREATE PROCEDURE”定义的存储过程。
- 可以不带括号地调用不带参数的存储过程。也就是说,“CALL p()”和“CALL p”是等效的。
- 【见:“CREATE PROCEDURE 和 CREATE FUNCTION 语句”】
CALL可以使用声明为OUT或INOUT参数的参数将值传递回其调用方。当过程返回时,Client 端程序也可以获得在例程中执行的最终语句所影响的行数:在 SQL 级别,调用“ROW_COUNT()”函数;从 C API 中,调用“mysql_affected_rows()”函数。
- 对于“INOUT”参数,在将其值传递给过程之前对其进行初始化。
- 在与“PREPARE”和“EXECUTE”一起使用的已准备好的 CALL 语句中,占位符可用于 IN 参数,OUT 和 INOUT 参数。
示例:以下过程具有一个 OUT 参数,该参数将该过程设置为当前服务器版本,以及一个 INOUT 值,该过程从其当前值开始加一:
CREATE PROCEDURE p (OUT ver_param VARCHAR(25), INOUT incr_param INT)
BEGIN
# Set value of OUT parameter
SELECT VERSION() INTO ver_param;
# Increment value of INOUT parameter
SET incr_param = incr_param + 1;
END;
在调用该过程之前,初始化要作为INOUT参数传递的变量。调用该过程后,将设置或修改两个变量的值:
mysql> SET @increment = 10;
mysql> CALL p(@version, @increment);
mysql> SELECT @version, @increment;
+------------------+------------+
| @version | @increment |
+------------------+------------+
| 5.7.20-debug-log | 11 |
+------------------+------------+
示例:在与 PREPARE 和 EXECUTE 一起使用的已准备好的 CALL 语句中,占位符可用于 IN 参数,OUT 和 INOUT 参数。这些类型的参数可以按如下方式使用:
mysql> SET @increment = 10;
mysql> PREPARE s FROM 'CALL p(?, ?)';
mysql> EXECUTE s USING @version, @increment;
mysql> SELECT @version, @increment;
+------------------+------------+
| @version | @increment |
+------------------+------------+
| 5.7.20-debug-log | 11 |
+------------------+------------+
- 关于“CLIENT_MULTI_RESULTS”:【???】
- 要编写使用CALL SQL 语句执行生成结果集的存储过程的 C 程序,必须启用“CLIENT_MULTI_RESULTS”标志。这是因为,除了过程中执行的语句可能返回的任何结果集之外,每个 CALL 还会返回指示呼叫状态的结果。如果将 CALL 用于执行任何包含准备好的语句的存储过程,则也必须启用“CLIENT_MULTI_RESULTS”。无法确定何时加载此类过程是否会产生这些语句,因此有必要假设它们会产生结果。
- 可以在调用“mysql_real_connect()”时启用“CLIENT_MULTI_RESULTS”,或者通过传递“CLIENT_MULTI_RESULTS”标志本身来显式,或者通过“CLIENT_MULTI_RESULTS”隐式传递(也将启用“CLIENT_MULTI_RESULTS”)。 “CLIENT_MULTI_RESULTS”默认启用。
- 要处理使用“mysql_query()”或“mysql_real_query()”执行的 CALL 语句的结果,请使用调用“mysql_next_result()”的循环来确定是否还有更多结果。【???】
- C 程序可以使用 Prepared-Statement 接口执行 CALL 语句并访问 OUT 和 INOUT 参数。这是通过使用调用“mysql_stmt_next_result()”的循环以确定是否还有更多结果来处理CALL语句的结果来完成的。【???】
DELETE 语句
- 单表语法:
DELETE [LOW_PRIORITY] [QUICK] [IGNORE] FROM tbl_name [PARTITION (partition_name [, partition_name] ...)] [WHERE where_condition] [ORDER BY ...] [LIMIT row_count]
- DELETE语句从 tbl_name 中删除行,并返回删除的行数。要检查已删除的行数,请调用“ROW_COUNT()”函数。
- 主要子句:
- “WHERE”子句:where_condition 是一个表达式,对于要删除的每一行,其值为 true;
- “ORDER BY”子句,则按指定的 Sequences 删除行;
- “LIMIT”子句:限制了可以删除的行数。
- (这些子句适用于单表删除,但不适用于多表删除)
- 多表语法:
DELETE [LOW_PRIORITY] [QUICK] [IGNORE] tbl_name[.*] [, tbl_name[.*]] ... FROM table_references [WHERE where_condition] DELETE [LOW_PRIORITY] [QUICK] [IGNORE] FROM tbl_name[.*] [, tbl_name[.*]] ... USING table_references [WHERE where_condition]
- 权限:
- 需要对表具有 DELETE 权限才能从中删除行。对于仅读取的任何列,例如 WHERE 子句中命名的列,只需要 SELECT 特权。
- 性能:
- 当不需要知道已删除的行数时,与没有 WHERE 子句的 DELETE 语句相比,“TRUNCATE TABLE”语句是清空表的更快方法。【见:“TRUNCATE TABLE 语句”】
- 与DELETE不同,TRUNCATE TABLE不能在事务内使用或在表上有锁时不能使用。
- 为了确保给定的 DELETE 语句不会花费太多时间,针对 DELETE 的特定于 MySQL 的“LIMIT row_count”子句指定了要删除的最大行数。如果要删除的行数大于限制,请重复 DELETE 语句,直到受影响的行数小于 LIMIT 值。【?】
- 当不需要知道已删除的行数时,与没有 WHERE 子句的 DELETE 语句相比,“TRUNCATE TABLE”语句是清空表的更快方法。【见:“TRUNCATE TABLE 语句”】
- 子查询:
- 您不能从 table 中删除,也不能从子查询的同一 table 中选择。
- 分区表支持:
- DELETE 使用“PARTITION”选项支持显式分区选择,该选项采用一个或多个分区或子分区(或两者)的逗号分隔名称列表,从中选择要删除的行。【未包括在列 table 中的分区将被忽略】
- 给定具有名为 p0 的分区的分区表 t,执行语句“DELETE FROM t PARTITION (p0)”与执行“ALTER TABLE t TRUNCATE PARTITION (p0)”具有相同的作用;在这两种情况下,都将删除分区 p0 中的所有行。
- PARTITION 可以与 WHERE 条件一起使用,在这种情况下,仅在列出的分区中的行上测试该条件。
- PARTITION 选项也可以在多表 DELETE 语句中使用。在 FROM 选项中命名的表中,最多可以使用一个这样的选项。
- DELETE 使用“PARTITION”选项支持显式分区选择,该选项采用一个或多个分区或子分区(或两者)的逗号分隔名称列表,从中选择要删除的行。【未包括在列 table 中的分区将被忽略】
- 自动递增列:
- 如果删除包含“AUTO_INCREMENT”列最大值的行,则该值不会再用于 MyISAM 或 InnoDB 表。
- 如果在 autocommit 模式下使用“DELETE FROM tbl_name”(不带WHERE子句)删除 table 中的所有行,则该序列将从 InnoDB 和 MyISAM 以外的所有存储引擎重新开始。
- 对于 MyISAM 表,可以在多列键中指定 AUTO_INCREMENT 个辅助列。在这种情况下,即使对于 MyISAM 表,也会重复使用从序列顶部删除的值。
- 修饰符:
- DELETE语句支持以下修饰符:
- 如果指定“LOW_PRIORITY”修饰符,则服务器将延迟 DELETE 的执行,直到没有其他 Client 端从 table 中读取。
- 这仅影响仅使用 table 级锁定(例如 MyISAM,MEMORY 和 MERGE)的存储引擎。
- 对于 MyISAM 表,如果使用“QUICK”修饰符,则存储引擎在删除期间不会合并索引叶,这可能会加快某些类型的删除操作。
- “IGNORE”修饰符使 MySQL 在删除行的过程中忽略错误。(在解析阶段遇到的错误将以通常的方式处理)由于使用 IGNORE 而被忽略的错误将作为警告返回。
- 删除顺序:
- 如果 DELETE 语句包含“ORDER BY”子句,则按该子句指定的 Sequences 删除行。【这主要与“LIMIT”结合使用】
- 示例:查找与 WHERE 子句匹配的行,按 timestamp_column 对其进行排序,并删除第一个(最旧的)行:
DELETE FROM somelog WHERE user = 'jcole' ORDER BY timestamp_column LIMIT 1;
- ORDER BY 还有助于按照避免引用完整性违规的 Sequences 删除行。
- InnoDB 表:
- 如果要从大 table 中删除许多行,则可能会超出 InnoDB 表的锁定表大小。为了避免此问题,或者只是为了最小化表保持锁定的时间,以下策略(根本不使用 DELETE)可能会有所帮助:
- 将未被删除的行插入到与原始表具有相同结构的空表中:
INSERT INTO t_copy SELECT * FROM t WHERE ... ;
- 使用“RENAME TABLE”(原子操作):将原始表移开,并将副本重命名为原始名称:
RENAME TABLE t TO t_old, t_copy TO t;
- 删除原始表:
DROP TABLE t_old;
- RENAME TABLE 执行时,没有其他会话可以访问涉及的表,因此重命名操作不会出现并发问题。
- MyISAM 表:【???】
- 在 MyISAM 表中,删除的行保留在链接列表中,随后的 INSERT 操作重用旧的行位置。要回收未使用的空间并减小文件大小,请使用“OPTIMIZE TABLE”语句或“myisamchk”Util 重新组织表。
- 【OPTIMIZE TABLE更易于使用,但myisamchk更快】
- “QUICK”修饰符影响索引叶是否合并以进行删除操作。“DELETE QUICK”对于将删除行的索引值替换为以后插入的行的相似索引值的应用程序最有用。在这种情况下,删除值留下的漏洞将被重用。【?】
- 当删除的值导致索引块填充不足,该索引块跨越索引值范围而又发生新插入时,“DELETE QUICK”无效。在这种情况下,使用“QUICK”会导致索引中浪费空间而无法回收。这是这种情况的示例:
- 创建一个包含索引的 AUTO_INCREMENT 列的表。
- 在表中插入许多行。每次插入都会产生一个索引值,该值将添加到索引的高端。
- 使用“DELETE QUICK”删除列范围低端的行块。
- 在这种情况下,与已删除索引值关联的索引块将被填充不足,但由于使用QUICK而不会与其他索引块合并。当发生新的插入操作时,它们仍会填充不足,因为新行的索引值不在删除范围内。此外,即使您以后使用DELETE而不使用QUICK,它们仍会填充不足,除非某些已删除的索引值恰好位于填充不足的块内或邻近的索引块中。要在这种情况下回收未使用的索引空间,请使用OPTIMIZE TABLE。
- 如果要从 table 中删除许多行,使用 DELETE QUICK 后跟 OPTIMIZE TABLE 可能会更快。这将重建索引,而不是执行许多索引块合并操作。
- 多表删除:
- 您可以在 DELETE 语句中指定多个表,以根据 WHERE 子句中的条件从一个或多个表中删除行。
- 不能在多表 DELETE 中使用 ORDER BY 或 LIMIT。
- 仅删除表中 FROM 子句前列出的“列”的匹配行:
DELETE t1, t2 FROM t1 INNER JOIN t2 INNER JOIN t3 WHERE t1.id=t2.id AND t2.id=t3.id;
- 仅删除 FROM 子句中列出的“表”中匹配的行(在 USING 子句之前):
- (同时从许多 table 中删除行,并使其他 table 仅用于搜索)
DELETE FROM t1, t2 USING t1 INNER JOIN t2 INNER JOIN t3 WHERE t1.id=t2.id AND t2.id=t3.id;
- 如果您使用涉及外键约束的 InnoDB 表的多表 DELETE 语句,则 MySQL 优化器可能以与其父/子关系不同的 Sequences 处理表。在这种情况下,该语句将失败并回滚。相反,您应该从单个表中删除,并依靠 InnoDB 提供的“ON DELETE”功能来相应地修改其他表。
- 关于使用别名:
- Correct:
DELETE a1, a2 FROM t1 AS a1 INNER JOIN t2 AS a2 WHERE a1.id=a2.id; DELETE FROM a1, a2 USING t1 AS a1 INNER JOIN t2 AS a2 WHERE a1.id=a2.id;
- Incorrect:
DELETE t1 AS a1, t2 AS a2 FROM t1 INNER JOIN t2 WHERE a1.id=a2.id; DELETE FROM t1 AS a1, t2 AS a2 USING t1 INNER JOIN t2 WHERE a1.id=a2.id;
DO 语句
DO expr [, expr] ...
执行表达式,但不返回任何结果。
- 在大多数方面,DO 是“SELECT expr, ...”的简写,但具有以下优点:当您不关心结果时,它会稍快一些。
- DO 主要用于具有副作用的功能,例如“RELEASE_LOCK()”。
- DO 仅执行表达式。不能在所有可以使用 SELECT 的情况下使用。例如,“DO id FROM t1”无效,因为它引用了一个 table。
示例:此 SELECT 语句暂停,但还会产生结果集:
mysql> SELECT SLEEP(5);
+----------+
| SLEEP(5) |
+----------+
| 0 |
+----------+
1 row in set (5.02 sec)
另一方面,DO暂停而不产生结果集:
mysql> DO SLEEP(5);
Query OK, 0 rows affected (4.99 sec)
这可能很有用,例如在存储函数或触发器中,该函数禁止生成结果集的语句。
HANDLER 语句
HANDLER tbl_name OPEN [ [AS] alias]
HANDLER tbl_name READ index_name { = | <= | >= | < | > } (value1,value2,...)
[ WHERE where_condition ] [LIMIT ... ]
HANDLER tbl_name READ index_name { FIRST | NEXT | PREV | LAST }
[ WHERE where_condition ] [LIMIT ... ]
HANDLER tbl_name READ { FIRST | NEXT }
[ WHERE where_condition ] [LIMIT ... ]
HANDLER tbl_name CLOSE
HANDLER 语句提供对表存储引擎接口的直接访问。【???】
- 它可用于InnoDB和MyISAM表。
- “HANDLER ... OPEN”:
- 打开一个 table,使其可以使用后续的“HANDLER ... READ”语句进行访问。
- 该 table 对象未被其他会话共享,并且在会话调用“HANDLER ... CLOSE”或会话终止之前不会关闭。
- 如果使用别名打开 table,则其他 HANDLER 语句对打开的 table 的进一步引用必须使用别名而不是 table 名。如果您不使用别名,而是使用数据库名称限定的 table 名称打开,则其他引用必须使用非限定的 table 名称。例如,对于使用 mydb.mytable 打开的 table,其他引用必须使用 mytable。
- “HANDLER ... READ”:【?】
- 第一种:获取一行,其中指定的索引满足给定值,并且满足 WHERE 条件。如果您有多列索引,则将索引列值指定为以逗号分隔的列 table。为索引中的所有列指定值,或者为索引列的最左前缀指定值。
- 假设索引 my_idx 依次包含三个名为 col_a,col_b 和 col_c 的列。 HANDLER 语句可以为索引中的所有三列或最左边的前缀中的列指定值。例如:
HANDLER ... READ my_idx = (col_a_val,col_b_val,col_c_val) ... HANDLER ... READ my_idx = (col_a_val,col_b_val) ... HANDLER ... READ my_idx = (col_a_val) ...
- 要使用 HANDLER 接口引用表的主键,请使用带引号的标识符“`PRIMARY`”:
HANDLER tbl_name READ `PRIMARY` ...
- 第二种:以与 WHERE 条件匹配的索引 Sequences 从 table 中获取一行。
- 第三种:以与 WHERE 条件匹配的自然行 Sequences 从 table 中获取一行。
- 当需要全 table 扫描时,它比“HANDLER tbl_name READ index_name”快。自然行 Sequences 是在 MyISAM 表数据文件中存储行的 Sequences。该语句也适用于 InnoDB 表,但是没有这样的概念,因为没有单独的数据文件。
- 如果没有 LIMIT 子句,则所有“HANDLER ... READ”形式都将获取一行。要返回特定数量的行,请包含 LIMIT 子句。它具有与 SELECT 语句相同的语法。
- 第一种:获取一行,其中指定的索引满足给定值,并且满足 WHERE 条件。如果您有多列索引,则将索引列值指定为以逗号分隔的列 table。为索引中的所有列指定值,或者为索引列的最左前缀指定值。
- “HANDLER ... CLOSE”:关闭使用“HANDLER ... OPEN”打开的 table。
使用 HANDLER 接口而不是普通的 SELECT 语句有多种原因:
- HANDLER 比 SELECT 快:
- 为“HANDLER ... OPEN”分配了一个指定的存储引擎处理程序对象。该对象可用于该 table 的后续 HANDLER 语句;它不需要为每个重新初始化。
- 涉及的解析较少。
- 没有优化程序或查询检查开销。
- 处理程序接口不必提供一致的数据外观(例如,允许dirty reads),因此存储引擎可以使用 SELECT 通常不允许的优化。
- HANDLER 使得移植到使用类似 ISAM 的底层接口的 MySQL 应用程序更加容易。
- HANDLER 使您可以用 SELECT 难以(甚至不可能)遍历数据库。当使用为数据库提供交互式用户界面的应用程序时,HANDLER 界面是查看数据的更自然的方式。
HANDLER是一个较低级别的语句。例如,它不提供一致性。也就是说,“HANDLER ... OPEN”不会 为 table 创建快照,并且不会锁定 table。这意味着发出“HANDLER ... OPEN”语句后,可以修改 table 数据(通过当前会话或其他会话),并且这些修改对于“HANDLER ... NEXT”或“HANDLER ... PREV”扫描可能仅部分可见。
可以关闭一个打开的处理程序并将其标记为重新打开,在这种情况下,处理程序将丢失其在 table 中的位置。当同时满足以下两种情况时,会发生这种情况:
- 任何会话都在处理程序的 table 上执行“FLUSH TABLES”或 DDL 语句。
- 打开处理程序的会话将执行使用 table 的非 HANDLER 语句。
表格的“TRUNCATE TABLE”关闭使用“HANDLER OPEN”打开的表格的所有处理程序。
- 如果用“FLUSH TABLES tbl_name WITH READ LOCK”刷新的 table 是用 HANDLER 打开的,则处理程序将隐式刷新,并丢失其位置。
INSERT 语句
INSERT [LOW_PRIORITY | DELAYED | HIGH_PRIORITY] [IGNORE]
[INTO] tbl_name
[PARTITION (partition_name [, partition_name] ...)]
[(col_name [, col_name] ...)]
{VALUES | VALUE} (value_list) [, (value_list)] ...
[ON DUPLICATE KEY UPDATE assignment_list]
INSERT [LOW_PRIORITY | DELAYED | HIGH_PRIORITY] [IGNORE]
[INTO] tbl_name
[PARTITION (partition_name [, partition_name] ...)]
SET assignment_list
[ON DUPLICATE KEY UPDATE assignment_list]
INSERT [LOW_PRIORITY | HIGH_PRIORITY] [IGNORE]
[INTO] tbl_name
[PARTITION (partition_name [, partition_name] ...)]
[(col_name [, col_name] ...)]
SELECT ...
[ON DUPLICATE KEY UPDATE assignment_list]
value:
{expr | DEFAULT}
value_list:
value [, value] ...
assignment:
col_name = value
assignment_list:
assignment [, assignment] ...
将新行插入到现有 table 中。
- 语句的“INSERT ... VALUES”和“INSERT ... SET”形式基于明确指定的值插入行。
- “INSERT ... SELECT”表单插入从另一个 table 或多个 table 中选择的行。
- 如果要插入的行将导致 UNIQUE 索引或P RIMARY KEY 中的重复值,则带有“ON DUPLICATE KEY UPDATE”子句的 INSERT 可以更新现有行。
- 在 MySQL 5.7 中,“DELAYED”关键字被接受但被服务器忽略。
- 当插入分区表时,您可以控制哪些分区和子分区接受新行。 “PARTITION”选项获取表的一个或多个分区或子分区(或两者)的逗号分隔名称列表。
- 可以使用“REPLACE”而不是 INSERT来 覆盖旧行。
- REPLACE 在处理新行时与“INSERT IGNORE”相对,该新行包含与旧行重复的唯一键值:新行替换了旧行,而不是被丢弃。
- 列名:
- 在表名后提供一个带括号的、用逗号分隔的列名列表,必须由“VALUES”列表或“SELECT”语句提供每个命名列的值。
- 如果您未指定“INSERT ... VALUES”或“INSERT ... SELECT”的列名列表,则 table 中每一列的值都必须由“VALUES”列表或“SELECT”语句提供。
- 如果您不知道 table 中各列的 Sequences,请使用“DESCRIBE tbl_name”进行查找。
- “SET”子句按名称显式指示列,以及用于分配每个列的值。
- 列值:
- 如果未启用严格 SQL 模式,则任何未明确指定值的列都将设置为其默认值(显式或隐式)。
- 如果启用了严格的 SQL 模式,则 INSERT 语句必须为没有默认值的每个列都指定一个显式值时。
- 如果列列表和“VALUES”列表均为空,则 INSERT 将创建一行并将每一列设置为其默认值:
INSERT INTO tbl_name () VALUES();
- 如果未启用严格模式,则 MySQL 对没有明确定义默认值的任何列使用隐式默认值。如果启用了严格模式,则任何列都没有默认值都会发生错误。
- 使用关键字“DEFAULT”可以将列明确设置为其默认值。
- 如果将生成的列显式插入,则唯一允许的值为“DEFAULT”。
- 在表达式中,您可以使用“DEFAULT(col_name)”来生成列 col_name 的默认值。
- 如果表达式数据类型与列数据类型不匹配,则可能会进行提供列值的表达式 expr 的类型转换。
- 给定值的转换可能导致不同的插入值,具体取决于列类型。
- 表达式 expr 可以引用值列表中先前设置的任何列。
- 例如,您可以这样做,因为 col2 的值引用 col1,该值先前已被分配:
INSERT INTO tbl_name (col1,col2) VALUES(15,col1*2);
- 但是以下内容不合法,因为 col1 的值引用 col2,该值是在 col1 之后分配的:
INSERT INTO tbl_name (col1,col2) VALUES(col2*2,15);
- 包含“AUTO_INCREMENT”值的列会发生异常。由于 AUTO_INCREMENT 值是在其他值分配之后生成的,因此对分配中 AUTO_INCREMENT 列的任何引用都将返回 0。
- 使用“VALUES”语法的 INSERT 语句可以插入多行。为此,请包括多个用逗号分隔的列值的列表,列表用括号括起来并用逗号分隔。
- 例:
INSERT INTO tbl_name (a,b,c) VALUES(1,2,3),(4,5,6),(7,8,9);
- 每个值列 table 必须包含与每行要插入的值一样多的值。如下无效语句:
INSERT INTO tbl_name (a,b,c) VALUES(1,2,3,4,5,6,7,8,9);
- 如果未启用严格 SQL 模式,则任何未明确指定值的列都将设置为其默认值(显式或隐式)。
- 受影响的行:可以使用“ROW_COUNT()” SQL 函数或“mysql_affected_rows()” C API 函数获得INSERT的受影响行值。
- 如果您使用带有多个值列表的“INSERT ... VALUES”或“INSERT ... SELECT”,则该语句以以下格式返回信息字符串:
Records: N1 Duplicates: N2 Warnings: N3
- 如果使用的是 C API,则可以通过调用“mysql_info()”函数获取信息字符串。
- 其中:“Records”表示该语句处理的行数。(这不一定是实际插入的行数,因为Duplicates可以为非零)“Duplicates”表示由于它们会复制某些现有唯一索引值而无法插入的行数。“Warnings”表示尝试插入以某种方式存在问题的列值的次数。在以下任何情况下都可能发生警告:
- 将“NULL”插入已声明为“NOT NULL”的列中。对于“多行 INSERT ”语句或“INSERT ... VALUES”语句,该列设置为列数据类型的隐式默认值。
- 对于数字类型,此值为0;对于字符串类型,此值为空字符串;对于日期和时间类型,其值为“零”。
- “INSERT ... SELECT”语句的处理方式与多行插入相同,因为服务器不会检查SELECT的结果集以查看其是否返回单行。
- 【对于“单行INSERT”,将“NULL”插入到“NOT NULL”列中时不会发生警告。而是,该语句失败并出现错误。】
- 将数字列设置为超出列范围的值。该值将被裁剪到范围的最近端点。
- 为数字列分配一个值,例如 '10.34 a'。尾随的非数字文本被去除,其余的数字部分被插入。如果字符串值没有前导数字部分,则该列设置为0。
- 将字符串插入超过该列最大长度的字符串列(CHAR,VARCHAR,TEXT或BLOB)中。该值将被截断为该列的最大长度。
- 在数据类型非法的日期或时间列中插入一个值。该列设置为该类型的适当零值。
- 如果 INSERT 在具有“AUTO_INCREMENT”列的 table 中插入一行,则可以使用“LAST_INSERT_ID()” SQL 函数或“mysql_insert_id()” C API 函数找到用于该列的值。
- 支持的修饰符:
- 如果使用“LOW_PRIORITY”修饰符,则 INSERT 的执行将延迟,直到没有其他 Client 端从 table 中读取为止。【这包括在现有 Client 端正在阅读时和“INSERT LOW_PRIORITY”语句正在 await 时开始阅读的其他 Client 端。因此,发出“INSERT LOW_PRIORITY”语句的 Client 端可能要 await 很长时间。】
- LOW_PRIORITY 仅影响仅使用表级锁定的存储引擎(例如 MyISAM,MEMORY 和 MERGE)。
- LOW_PRIORITY 通常不应与 MyISAM 一起使用,因为这样做会禁用并发插入。
- 如果指定“HIGH_PRIORITY”,并且服务器以该选项启动时,它将覆盖“--low-priority-updates”选项的效果。这还会导致不使用并发插入。
- HIGH_PRIORITY 仅影响仅使用表级锁定的存储引擎(例如 MyISAM,MEMORY 和 MERGE)。
- 如果使用“IGNORE”修饰符,则在执行 INSERT 语句时发生的错误将被忽略。
- 例如,如果没有 IGNORE,则复制表中现有 UNIQUE 索引或 PRIMARY KEY 值的行将导致重复键错误,并且该语句将中止。使用 IGNORE,该行将被丢弃,并且不会发生错误。被忽略的错误会生成警告。
- 如果未指定 IGNORE,那些“触发错误的”数据转换将中止该语句。使用 IGNORE,无效值将调整为最接近的值并插入;产生警告,但该语句不会中止。【您可以使用“mysql_info()” C API 函数确定表中实际插入了多少行。】
- 如果指定“ON DUPLICATE KEY UPDATE”,并且插入的行会导致“UNIQUE”索引或“PRIMARY KEY”中的值重复,则会出现旧行的 UPDATE。
- 如果将行作为新行插入,则每行的受影响行值为 1;如果更新了现有行,则为 2;如果将现有行设置为其当前值,则为 0。
- 如果在连接到mysqld时为“mysql_real_connect()” C API 函数指定“CLIENT_FOUND_ROWS”标志,则在将现有行设置为当前值的情况下,受影响的行值为 1(而不是 0)。
- “INSERT DELAYED”在 MySQL 5.6 中已弃用,并计划最终删除。在 MySQL 5.7 中,“DELAYED”修饰符被接受但被忽略。改用 INSERT(不带 DELAYED)。
- 如果使用“LOW_PRIORITY”修饰符,则 INSERT 的执行将延迟,直到没有其他 Client 端从 table 中读取为止。【这包括在现有 Client 端正在阅读时和“INSERT LOW_PRIORITY”语句正在 await 时开始阅读的其他 Client 端。因此,发出“INSERT LOW_PRIORITY”语句的 Client 端可能要 await 很长时间。】
- 使用“使用表级锁的存储引擎”(例如 MyISAM)来影响分区表的 INSERT 语句仅锁定那些实际插入行的分区。
INSERT ... SELECT
INSERT [LOW_PRIORITY | HIGH_PRIORITY] [IGNORE]
[INTO] tbl_name
[PARTITION (partition_name [, partition_name] ...)]
[(col_name [, col_name] ...)]
SELECT ...
[ON DUPLICATE KEY UPDATE assignment_list]
value:
{expr | DEFAULT}
assignment:
col_name = value
assignment_list:
assignment [, assignment] ...
根据SELECT语句的结果快速向 table 中插入许多行,该语句可以从一个或多个 table 中进行选择。
- 示例:
INSERT INTO tbl_temp2 (fld_id) SELECT tbl_temp1.fld_order_id FROM tbl_temp1 WHERE tbl_temp1.fld_order_id > 100;
- 以下条件适用于“INSERT ... SELECT”语句:
- 指定“IGNORE”可忽略将导致重复键冲突的行。
- INSERT 语句的目标 table 可能出现在查询的 SELECT 部分的FROM子句中。但是,您不能在子查询中从 table 中选择并插入同一 table 。
- 【从同一 table 中选择并插入到同一 table 中时,MySQL 创建一个内部临时 table 来保存 SELECT 中的行,然后将这些行插入目标 table 中。】但是,当 t 是 TEMPORARY table 时,不能使用“INSERT INTO t ... SELECT ... FROM t”,因为 TEMPORARY table 不能在同一语句中被两次引用。
- AUTO_INCREMENT 列照常工作。
- 为了确保二进制日志可用于重新创建原始 table,MySQL 不允许并发插入“INSERT ... SELECT”语句。
- 为了避免当 SELECT 和 INSERT 引用同一 table 时列引用存在歧义,请为 SELECT 部分中使用的每个 table 提供唯一的别名,并使用适当的别名来限定该部分中的列名。
- 分区:
- 您可以通过 table 名后面的“PARTITION”选项显式选择要使用源 table 或目标 table(或两者)的分区或子分区(或两者):
- 在语句的 SELECT 部分中将 PARTITION 与源 table 的名称一起使用时,仅从其分区列表中命名的分区或子分区中选择行。
- 如果在语句的 INSERT 部分中将 PARTITION 与目标 table 的名称一起使用,则必须可以将所有选择的行插入到该选项之后的分区列表中命名的分区或子分区中。否则,INSERT ... SELECT语句将失败。
- ORDER BY:
- 没有“ORDER BY”子句的SELECT语句返回行的 Sequences 是不确定的。这意味着,在使用复制时,无法保证这样的SELECT返回主服务器和从服务器上的行的 Sequences 相同,这可能导致它们之间的不一致。
- 为了防止这种情况的发生,请始终使用要在主服务器和从属服务器上产生相同行 Sequences 的 ORDER BY 子句编写要复制的“INSERT ... SELECT”语句。
- 由于此问题,对于基于语句的复制,“INSERT ... SELECT ON DUPLICATE KEY UPDATE”和“INSERT IGNORE ... SELECT”语句被标记为不安全。当使用基于语句的模式时,此类语句在错误日志中产生警告,而在使用“MIXED”模式时,则使用基于行的格式将其写入二进制日志。【???】
- 锁:
- “INSERT ... SELECT”语句使用诸如表级锁之类的存储引擎(如 MyISAM)来影响分区表,该存储引擎采用表级锁来锁定目标 table 的所有分区。但是,只有那些实际从源 table 读取的分区才被锁定。
- (对于使用诸如 InnoDB 之类的存储引擎并采用行级锁定的 table,不会发生这种情况。)
INSERT ... ON DUPLICATE KEY UPDATE
如果指定“ON DUPLICATE KEY UPDATE”子句,而要插入的行将导致“UNIQUE”索引或“PRIMARY KEY”中的值重复,则会出现旧行的 UPDATE。【duplicate:['djuːplɪkeɪt] 复制;副本;】
例如,如果列 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;
INSERT DELAYED
INSERT DELAYED ...
INSERT语句的“DELAYED”选项是标准 SQL 的 MySQL 扩展。在 MySQL 的早期版本中,它可以用于某些类型的表(例如 MyISAM),这样,当 Client 端使用“INSERT DELAYED”时,它会立即从服务器上获得成功,并且该行排队 await 在 table 插入时插入未被其他任何线程使用。
- DELAYED 插入和替换在 MySQL 5.6 中已弃用。在 MySQL 5.7 中,不支持DELAYED。服务器识别但忽略DELAYED关键字,将插入作为非延迟插入进行处理,并生成“ER_WARN_LEGACY_SYNTAX_CONVERTED”警告(“不再支持 INSERT DELAYED.该语句已转换为 INSERT”)。 DELAYED关键字计划在将来的版本中删除。