MySQL 对标准 SQL 的扩展
MySQL 服务器支持一些在其他 SQL 数据库管理系统中不太可能找到的扩展功能。请注意,如果您使用了这些扩展,您的代码很可能无法移植到其他 SQL 服务器。在某些情况下,您可以通过使用以下形式的注释来编写包含 MySQL 扩展但仍可移植的代码:
/*! MySQL 特定的代码 */
在这种情况下,MySQL 服务器会像解析执行其他 SQL 语句一样,解析并执行注释内的代码,而其他 SQL 服务器则应忽略这些扩展。例如,MySQL 服务器能识别以下语句中的
STRAIGHT_JOIN 关键字,但其他服务器不应识别:
`SELECT /*! STRAIGHT_JOIN */ col1 FROM table1,table2 WHERE ...`
如果在
! 字符后添加版本号,则仅当 MySQL 版本大于或等于指定版本号时,才会执行注释内的语法。以下注释中的
KEY_BLOCK_SIZE 子句仅由 MySQL 5.1.10 或更高版本的服务器执行:
`CREATE TABLE t1(a INT, KEY (a)) /*!50110 KEY_BLOCK_SIZE=1024 */;`
磁盘上的数据组织
- MySQL 服务器将每个数据库映射到 MySQL 数据目录下的一个子目录,并将数据库中的表映射到该数据库目录中的文件名。因此,在具有区分大小写文件名的操作系统(如大多数 Unix 系统)上,MySQL 服务器中的数据库名和表名是 区分大小写的。参见第 11.2.3 节,“标识符的大小写敏感性”。
mysql> create database cjc; Query OK, 1 row affected (3.33 sec) mysql> system ls -lrth /mysqldata/13309/data/ total 1.1G ...... drwxr-x--- 2 mysql mysql 6 Jan 25 13:31 cjc
通用语言语法
- 默认情况下,字符串可以用
"或'引起来。如果启用了ANSI_QUOTESSQL 模式,则字符串只能由'引起,服务器会将由"引起的字符串解释为标识符。 -
\是字符串中的转义字符。 - 在 SQL 语句中,您可以使用
db_name.tbl_name语法访问不同数据库中的表。一些 SQL 服务器提供相同的功能,但称之为“用户空间”。MySQL 服务器不支持表空间,例如在类似这样的语句中使用:CREATE TABLE ralph.my_table ... IN my_tablespace。
SQL 语句语法
01.ANALYZE TABLE等
-
ANALYZE TABLE、CHECK TABLE、OPTIMIZE TABLE和REPAIR TABLE语句。
ANALYZE TABLE
ANALYZE TABLE 生成表统计信息:
不带任何 HISTOGRAM 子句的 ANALYZE TABLE 执行键分布分析,并将指定表或表的分布存储起来。对于 MyISAM 表,用于键分布分析的 ANALYZE TABLE 等同于使用 myisamch k --analyze。
带有 UPDATE HISTOGRAM 子句的 ANALYZE TABLE 为指定的表列生成直方图统计信息,并将其存储在数据字典中。此语法只允许一个表名。MySQL 8.0.31 及更高版本还支持将单个列的直方图设置为用户定义的 JSON 值。
带有 DROP HISTOGRAM 子句的 ANALYZE TABLE 从数据字典中移除指定表列的直方图统计信息。此语法只允许一个表名。
此语句要求对表具有 SELECT 和 INSERT 权限。
ANALYZE TABLE 适用于 InnoDB、NDB 和 MyISAM 表。它不适用于视图。
如果启用了 innodb_read_only 系统变量,ANALYZE TABLE 可能会失败,因为它无法更新数据字典中使用 InnoDB
的统计表。对于更新键分布的 ANALYZE TABLE 操作,即使操作更新了表本身(例如,如果是 MyISAM
表),也可能发生失败。要获取更新后的分布统计信息,请设置 information_schema_stats_expiry=0。
ANALYZE TABLE 支持分区表,您可以使用 ALTER TABLE … ANALYZE PARTITION 来分析一个或多个分区;更多信息,请参见第 15.1.9 节“ALTER TABLE 语句”和第 26.3.4 节“分区的维护”。
分析期间,对于 InnoDB 和 MyISAM 表,会使用读锁锁定表。
默认情况下,服务器将 ANALYZE TABLE 语句写入二进制日志,以便复制到副本。要禁止记录日志,请指定可选的 NO_WRITE_TO_BINLOG 关键字或其别名 LOCAL。
以前,ANALYZE TABLE 需要一个刷新锁。这意味着,当调用 ANALYZE TABLE
时,如果有长时间运行的语句或事务仍在使用该表,则任何后续的语句和事务必须等待这些操作完成才能释放刷新锁。此问题在 MySQL
8.0.24(及更高版本)中得到解决,其中 ANALYZE TABLE 不再导致后续操作等待。
示例如下:
ANALYZE [NO_WRITE_TO_BINLOG | LOCAL] TABLE tbl_name [, tbl_name] ... ANALYZE [NO_WRITE_TO_BINLOG | LOCAL] TABLE tbl_name UPDATE HISTOGRAM ON col_name [, col_name] ... [WITH N BUCKETS] ANALYZE [NO_WRITE_TO_BINLOG | LOCAL] TABLE tbl_name UPDATE HISTOGRAM ON col_name [USING DATA 'json_data'] ANALYZE [NO_WRITE_TO_BINLOG | LOCAL] TABLE tbl_name DROP HISTOGRAM ON col_name [, col_name] ...
mysql> create table t1(id int,name varchar(10),time time); Query OK, 0 rows affected (0.32 sec) mysql> insert into cjc.t1 values(1,'x',now()),(2,'y',now()); Query OK, 2 rows affected (0.32 sec) Records: 2 Duplicates: 0 Warnings: 0 mysql> select * from cjc.t1; +------+------+----------+ | id | name | time | +------+------+----------+ | 1 | x | 13:33:21 | | 2 | y | 13:33:21 | +------+------+----------+ 2 rows in set (0.00 sec)
mysql> ANALYZE TABLE t1;
+--------+---------+----------+----------+
| Table | Op | Msg_type | Msg_text |
+--------+---------+----------+----------+
| cjc.t1 | analyze | status | OK |
+--------+---------+----------+----------+
1 row in set (0.01 sec)
mysql> ANALYZE TABLE t1 UPDATE HISTOGRAM ON name;
+--------+-----------+----------+-------------------------------------------------+
| Table | Op | Msg_type | Msg_text |
+--------+-----------+----------+-------------------------------------------------+
| cjc.t1 | histogram | status | Histogram statistics created for column 'name'. |
+--------+-----------+----------+-------------------------------------------------+
1 row in set (0.31 sec
*************************** 1. row ***************************
SCHEMA_NAME: cjc
TABLE_NAME: t1
COLUMN_NAME: name
HISTOGRAM: {"buckets": [["base64:type254:eA==", 0.5], ["base64:type254:eQ==", 1.0]], "data-type": "string", "null-values": 0.0, "collation-id": 45, "last-updated": "2026-01-25 05:34:30.092510", "sampling-rate": 1.0, "histogram-type": "singleton", "number-of-buckets-specified": 100}
1 row in set (0.00 sec)
ANALYZE TABLE t UPDATE HISTOGRAM ON c1;
ANALYZE TABLE t UPDATE HISTOGRAM ON c1, c2, c3 WITH 10 BUCKETS;
ANALYZE TABLE t UPDATE HISTOGRAM ON c1, c3 WITH 10 BUCKETS;
ANALYZE TABLE t DROP HISTOGRAM ON c2;
ANALYZE TABLE t UPDATE HISTOGRAM ON c1
USING DATA '{"buckets": [[206, 0.0625], [456, 0.125], [608, 0.1875]],
"data-type": "int", "null-values": 0.0, "collation-id":
8, "last-updated": "2022-10-11 16:13:14.563319",
"sampling-rate": 1.0, "histogram-type": "singleton",
"number-of-buckets-specified": 100}'
SET histogram_generation_max_mem_size = 2000000;
ANALYZE TABLE employees UPDATE HISTOGRAM ON birth_date WITH 16 BUCKETS\G
查看:
TABLE information_schema.column_statistics\G SELECT HISTOGRAM->>'$."sampling-rate"' FROM INFORMATION_SCHEMA.COLUMN_STATISTICS WHERE TABLE_NAME = "employees" AND COLUMN_NAME = "birth_date";
CHECK TABLE
CHECK TABLE 检查一个或多个表的错误。CHECK TABLE 还可以检查视图是否存在问题,例如视图定义中引用的表已不存在。
要检查表,您必须对其拥有某些权限。
CHECK TABLE 适用于 InnoDB、MyISAM、ARCHIVE 和 CSV 表。
在对 InnoDB 表运行 CHECK TABLE 之前,请参阅 InnoDB 表的 CHECK TABLE 使用说明。
CHECK TABLE 支持分区表,您可以使用 ALTER TABLE … CHECK PARTITION 来检查一个或多个分区;更多信息,请参见第 15.1.9 节“ALTER TABLE 语句”和第 26.3.4 节“分区的维护”。
CHECK TABLE 会忽略未建立索引的虚拟生成列。

示例如下:
CHECK TABLE tbl_name [, tbl_name] ... [option] ...
option: {
FOR UPGRADE
| QUICK
| FAST
| MEDIUM
| EXTENDED
| CHANGED
}
CHECK TABLE test_table FAST QUICK;
mysql> check table cjc.t1;
±-------±------±---------±---------+
| Table | Op | Msg_type | Msg_text |
±-------±------±---------±---------+
| cjc.t1 | check | status | OK |
±-------±------±---------±---------+
1 row in set (0.00 sec)
mysql> check table cjc.t1 FAST QUICK;
±-------±------±---------±---------+
| Table | Op | Msg_type | Msg_text |
±-------±------±---------±---------+
| cjc.t1 | check | status | OK |
±-------±------±---------±---------+
1 row in set (0.01 sec)
CHECKSUM TABLE
CHECKSUM TABLE 报告表内容的校验和。您可以使用此语句验证在备份、回滚或其他旨在将数据恢复到已知状态的操作前后,内容是否相同。
此语句要求对表具有 SELECT 权限。
此语句不支持视图。如果您对视图运行 CHECKSUM TABLE,校验和值始终为 NULL,并返回一条警告。
对于不存在的表,CHECKSUM TABLE 返回 NULL 并生成一条警告。
在校验和操作期间,对于 InnoDB 和 MyISAM 表,会使用读锁锁定表。
性能考量
默认情况下,系统会逐行读取整个表并计算校验和。对于大型表,这可能需要很长时间,因此您应仅偶尔执行此操作。这种逐行计算是使用 EXTENDED
子句时(对于 InnoDB 和除 MyISAM 外的所有其他存储引擎),以及对于未使用 CHECKSUM=1 子句创建的 MyISAM
表时所采用的方式。
对于使用 CHECKSUM=1 子句创建的 MyISAM 表,CHECKSUM TABLE 或 CHECKSUM TABLE … QUICK
会返回可以非常快速返回的“实时”表校验和。如果表不满足所有这些条件,QUICK 方法将返回 NULL。InnoDB 表不支持 QUICK
方法。有关 CHECKSUM 子句的语法,请参见第 15.1.20 节“CREATE TABLE 语句”。
校验和值取决于表的行格式。如果行格式改变,校验和也会改变。例如,在 MySQL 5.6.5 之前,MySQL 5.6 中诸如
TIME、DATETIME 和 TIMESTAMP 之类的时间类型的存储格式发生了变化,因此如果将 5.5 表升级到 MySQL
5.6,校验和值可能会改变。
重要提示
如果两个表的校验和不同,则几乎可以确定这两个表在某种程度上存在差异。但是,由于 CHECKSUM TABLE 使用的哈希函数不能保证完全避免冲突,因此存在极小的可能性,两个不完全相同的表可能产生相同的校验和。
语法:
CHECKSUM TABLE tbl_name [, tbl_name] ... [QUICK | EXTENDED]
mysql> create table cjc.t2 as select * from cjc.t1; Query OK, 2 rows affected (0.29 sec) Records: 2 Duplicates: 0 Warnings: 0 mysql> checksum table cjc.t1,cjc.t2; +--------+------------+ | Table | Checksum | +--------+------------+ | cjc.t1 | 1202944216 | | cjc.t2 | 1202944216 | +--------+------------+ 2 rows in set (0.00 sec) mysql> insert into cjc.t2 values(3,'z',now()); Query OK, 1 row affected (0.30 sec) mysql> checksum table cjc.t1,cjc.t2; +--------+------------+ | Table | Checksum | +--------+------------+ | cjc.t1 | 1202944216 | | cjc.t2 | 1872494055 | +--------+------------+ 2 rows in set (0.00 sec)
OPTIMIZE TABLE
OPTIMIZE TABLE 重组表数据及相关索引数据的物理存储,以减少存储空间并提高访问表时的 I/O 效率。对每个表所做的具体更改取决于该表所使用的存储引擎。
根据表的类型,在以下情况下使用 OPTIMIZE TABLE:
- 对具有独立 .ibd 文件的 InnoDB 表(因创建时启用了 innodb_file_per_table 选项)执行了大量插入、更新或删除操作后。表及索引将被重组,并且可以回收磁盘空间供操作系统使用。
- 对 InnoDB 表中属于 FULLTEXT 索引的列执行了大量插入、更新或删除操作后。需首先设置配置选项 innodb_optimize_fulltext_only=1。为使索引维护时间保持在合理范围内,可设置 innodb_ft_num_word_optimize 选项以指定在搜索索引中更新的单词数量,并运行一系列 OPTIMIZE TABLE 语句,直至搜索索引完全更新。
- 删除了 MyISAM 或 ARCHIVE 表的大部分内容,或对具有可变长度行(包含 VARCHAR、VARBINARY、BLOB 或 TEXT 列的表)的 MyISAM 或 ARCHIVE 表进行了大量更改后。已删除的行会维护在一个链表中,后续的 INSERT 操作会复用旧的行位置。您可以使用 OPTIMIZE TABLE 来回收未使用的空间并对数据文件进行碎片整理。在对表进行大量更改后,此语句还可能提高使用该表的语句的性能,有时提升显著。
此语句要求对表具有 SELECT 和 INSERT 权限。
OPTIMIZE TABLE 适用于 InnoDB、MyISAM 和 ARCHIVE 表。OPTIMIZE TABLE 也支持内存 NDB
表的动态列。它不适用于内存表的固定宽度列,也不适用于磁盘数据表。NDB 集群表上 OPTIMIZE 的性能可以通过
--ndb-optimization-delay 进行调优,该参数控制 OPTIMIZE TABLE
处理行批次之间的等待时间。更多信息,请参见第 25.2.7.11 节“在 NDB Cluster 8.0 中已解决的早期 NDB Cluster
问题”。
对于 NDB 集群表,OPTIMIZE TABLE 可能会被中断(例如,通过终止执行 OPTIMIZE 操作的 SQL 线程)。
默认情况下,OPTIMIZE TABLE 不适用于使用任何其他存储引擎创建的表,并返回一个表示不支持的结果。您可以通过使用 --skip-new
选项启动 mysqld 来使 OPTIMIZE TABLE 适用于其他存储引擎。在这种情况下,OPTIMIZE TABLE 仅被映射到
ALTER TABLE。
此语句不适用于视图。
OPTIMIZE TABLE 支持分区表。有关将此语句与分区表和表分区结合使用的信息,请参见第 26.3.4 节“分区的维护”。
默认情况下,服务器将 OPTIMIZE TABLE 语句写入二进制日志,以便复制到副本。要禁止记录日志,请指定可选的 NO_WRITE_TO_BINLOG 关键字或其别名 LOCAL。
InnoDB 详情
对于 InnoDB 表,OPTIMIZE TABLE 映射到 ALTER TABLE … FORCE,该操作会重建表以更新索引统计信息并释放聚集索引中未使用的空间。在 InnoDB 表上运行 OPTIMIZE TABLE 时,输出中会显示此信息,如下所示:
mysql> OPTIMIZE table cjc.t1; +--------+----------+----------+-------------------------------------------------------------------+ | Table | Op | Msg_type | Msg_text | +--------+----------+----------+-------------------------------------------------------------------+ | cjc.t1 | optimize | note | Table does not support optimize, doing recreate + analyze instead | | cjc.t1 | optimize | status | OK | +--------+----------+----------+-------------------------------------------------------------------+ 2 rows in set (0.50 sec)
对于常规和分区的 InnoDB 表,OPTIMIZE TABLE 使用在线 DDL,这减少了对并发 DML 操作造成的停机时间。由 OPTIMIZE TABLE 触发的表重建是原地完成的。独占表锁仅在操作的准备阶段和提交阶段短暂持有。在准备阶段,元数据被更新,并创建一个中间表。在提交阶段,表元数据的变更被提交。
在以下条件下,OPTIMIZE TABLE 会使用表复制方法来重建表:
- 当启用了 old_alter_table 系统变量时。
- 当服务器使用 --skip-new 选项启动时。
对于包含 FULLTEXT 索引的 InnoDB 表,不支持使用在线 DDL 的 OPTIMIZE TABLE。而是使用表复制方法。
InnoDB 使用页面分配 方法存储数据,不会像传统存储引擎(如 MyISAM)那样遭受碎片化问题。在考虑是否运行优化操作时,需考虑服务器预期处理的事务工作负载:
- 一定程度的碎片化是预期的。InnoDB 仅将页面填充到 93% 满,以便为更新留出空间而无需分裂页面。
- 删除操作可能会留下间隙,导致页面填充度低于预期,这可能使得优化表变得有价值。
- 当有足够空间时,对行的更新通常会在同一页面内重写数据,具体取决于数据类型和行格式。参见第 17.9.1.5 节“InnoDB 表的压缩原理”和第 17.10 节“InnoDB 行格式”。
- 高并发工作负载可能会随着时间的推移在索引中留下间隙,因为 InnoDB 通过其 MVCC 机制保留了同一数据的多个版本。参见第 17.3 节“InnoDB 多版本控制”。
MyISAM 详情
对于 MyISAM 表,OPTIMIZE TABLE 按以下方式工作:
- 如果表有删除或拆分的行,则修复该表。
- 如果索引页未排序,则对它们进行排序。
- 如果表的统计信息不是最新的(且无法通过排序索引完成修复),则更新它们。
其他注意事项
对于常规和分区的 InnoDB 表,OPTIMIZE TABLE 是在线执行的。否则,MySQL 在 OPTIMIZE TABLE 运行期间会锁定表。
OPTIMIZE TABLE 不会对 R 树索引进行排序,例如 POINT 列上的空间索引。(Bug #23578)
REPAIR TABLE
语法:
REPAIR [NO_WRITE_TO_BINLOG | LOCAL] TABLE tbl_name [, tbl_name] ... [QUICK] [EXTENDED] [USE_FRM]
REPAIR TABLE 修复可能损坏的表,仅适用于某些存储引擎。
此语句要求对表具有 SELECT 和 INSERT 权限。
虽然通常您绝不应该需要运行 REPAIR TABLE,但如果发生灾难,此语句很可能从 MyISAM
表中恢复您的所有数据。如果您的表经常损坏,请尝试找出原因,以消除使用 REPAIR TABLE 的必要。参见第 B.3.3.3 节“如果
MySQL 持续崩溃该怎么办”以及第 18.2.4 节“MyISAM 表问题”。
REPAIR TABLE 会检查表以确定是否需要升级。如果需要,它将执行升级,遵循与 CHECK TABLE … FOR UPGRADE 相同的规则。更多信息,请参见第 15.7.3.2 节“CHECK TABLE 语句”。
重要提示
- 在执行表修复操作之前,请对表进行备份;在某些情况下,该操作可能导致数据丢失。可能的原因包括但不限于文件系统错误。参见第 9 章“备份与恢复”。
- 如果服务器在 REPAIR TABLE 操作期间退出,重启后至关重要的是,在对该表执行任何其他操作之前立即对其执行另一个 REPAIR TABLE 语句。在最坏的情况下,您可能得到一个没有数据文件信息的新建干净索引文件,然后您执行的下一个操作可能会覆盖数据文件。这种情况不太可能发生,但可能性存在,这凸显了首先进行备份的价值。
- 如果源上的表损坏,并且您对其运行了 REPAIR TABLE,则对原始表的任何更改不会传播到副本。
REPAIR TABLE 存储引擎及分区支持
REPAIR TABLE 选项
REPAIR TABLE 输出
表修复注意事项
REPAIR TABLE 存储引擎及分区支持
REPAIR TABLE 适用于 MyISAM、ARCHIVE 和 CSV 表。对于 MyISAM 表,默认情况下其效果与
myisamch k --recover tbl_name 相同。此语句不适用于视图。
REPAIR TABLE 支持分区表。但是,USE_FRM 选项不能在此语句中用于分区表。
您可以使用 ALTER TABLE … REPAIR PARTITION 来修复一个或多个分区;更多信息,请参见第 15.1.9 节“ALTER TABLE 语句”和第 26.3.4 节“分区的维护”。
REPAIR TABLE 选项
-
NO_WRITE_TO_BINLOG 或 LOCAL
默认情况下,服务器将 REPAIR TABLE 语句写入二进制日志,以便它们复制到副本。要禁止记录日志,请指定可选的 NO_WRITE_TO_BINLOG 关键字或其别名 LOCAL。 -
QUICK
如果使用 QUICK 选项,REPAIR TABLE 仅尝试修复索引文件,而不修复数据文件。此类修复类似于myisamch k --recover --quick所做的修复。 -
EXTENDED
如果使用 EXTENDED 选项,MySQL 会逐行创建索引,而不是通过排序一次性创建一个索引。此类修复类似于myisamch k --safe-recover所做的修复。 -
USE_FRM
如果 .MYI 索引文件丢失或其头部损坏,可以使用 USE_FRM 选项。此选项告诉 MySQL 不要信任 .MYI 文件头部的信息,而是使用数据字典中的信息重新创建它。这种修复无法使用 myisamch k 完成。警告
仅当无法使用常规 REPAIR 模式时,才使用 USE_FRM 选项。告诉服务器忽略 .MYI 文件会使修复过程无法访问存储在 .MYI 中的重要表元数据,这可能会产生有害后果:- 当前的 AUTO_INCREMENT 值会丢失。
- 表中已删除记录的链接会丢失,这意味着此后已删除记录的空闲空间将保持未占用状态。
- .MYI 头部指示表是否被压缩。如果服务器忽略此信息,则无法判断表是否被压缩,修复可能导致表内容更改或丢失。这意味着不应将 USE_FRM 用于压缩表。无论如何,这应该没有必要:压缩表是只读的,因此它们不应该损坏。
如果对由与当前运行的 MySQL 服务器版本不同的版本创建的表使用 USE_FRM,REPAIR TABLE 不会尝试修复该表。在这种情况下,REPAIR TABLE 返回的结果集 会包含一行,其 Msg_type 值为 error,Msg_text 值为Failed repairing incompatible .FRM file。
如果使用了 USE_FRM,REPAIR TABLE 不会检查表以确定是否需要升级。
REPAIR TABLE 输出
REPAIR TABLE 返回一个结果集,包含下表中所示的列。

REPAIR TABLE 语句可能会为每个修复的表生成多行信息。最后一行具有 Msg_type 值 status,且 Msg_text 通常应为 OK。对于 MyISAM 表,如果未得到 OK,应尝试使用
myisamch k --safe-recover 进行修复。(REPAIR TABLE 未实现 myisamch k 的所有选项。使用
myisamch k --safe-recover,您还可以使用 REPAIR TABLE 不支持的选项,例如
--max-record-length。)
REPAIR TABLE 会捕获并抛出在将表统计信息从旧的损坏文件复制到新创建的文件时发生的任何错误。例如,如果 .MYD 或 .MYI 文件的所有者的用户 ID 与 mysqld 进程的用户 ID 不同,除非 mysqld 由 root 用户启动,否则 REPAIR TABLE 会生成“无法更改文件的所有权”错误。
表修复注意事项
如果表包含 5.6.4 之前格式的旧时间类型列(不支持小数秒精度的 TIME、DATETIME 和 TIMESTAMP 列)且
avoid_temporal_upgrade 系统变量被禁用,REPAIR TABLE 会升级该表。如果
avoid_temporal_upgrade 已启用,REPAIR TABLE 会忽略表中存在的旧时间类型列并且不会升级它们。
要升级包含此类时间类型列的表,请在执行 REPAIR TABLE 之前禁用
avoid_temporal_upgrade。
通过设置某些系统变量,您或许可以提高 REPAIR TABLE 的性能。参见第 10.6.3 节“优化 REPAIR TABLE 语句”。
mysql> REPAIR TABLE cjc.t1; +--------+--------+----------+---------------------------------------------------------+ | Table | Op | Msg_type | Msg_text | +--------+--------+----------+---------------------------------------------------------+ | cjc.t1 | repair | note | The storage engine for the table doesn't support repair | +--------+--------+----------+---------------------------------------------------------+ 1 row in set (0.29 sec)
02.CREATE DATABASE等
-
CREATE DATABASE、DROP DATABASE和ALTER DATABASE语句。。
CREATE DATABASE
语法:
CREATE {DATABASE | SCHEMA} [IF NOT EXISTS] db_name
[create_option] ...
create_option: [DEFAULT] {
CHARACTER SET [=] charset_name
| COLLATE [=] collation_name
| ENCRYPTION [=] {'Y' | 'N'}
}
CREATE DATABASE 以给定名称创建数据库。使用此语句需要对该数据库拥有 CREATE 权限。CREATE SCHEMA 是 CREATE DATABASE 的同义词。
如果数据库已存在且未指定 IF NOT EXISTS,则会发生错误。
在存在活跃的 LOCK TABLES 语句的会话中不允许使用 CREATE DATABASE。
每个 create_option 指定一个数据库特性。数据库特性存储在数据字典中。
- CHARACTER SET 选项指定默认的数据库字符集。COLLATE 选项指定默认的数据库排序规则。有关字符集和排序规则名称的信息,请参见第 12 章“字符集、排序规则、Unicode”。
- 要查看可用的字符集和排序规则,请分别使用 SHOW CHARACTER SET 和 SHOW COLLATION 语句。参见第 15.7.7.3 节“SHOW CHARACTER SET 语句”和第 15.7.7.4 节“SHOW COLLATION 语句”。
- ENCRYPTION 选项(在 MySQL 8.0.16 中引入)定义默认的数据库加密设置,该设置由在该数据库中创建的表继承。允许的值为 ‘Y’(启用加密)和 ‘N’(禁用加密)。如果未指定 ENCRYPTION 选项,则 default_table_encryption 系统变量的值定义默认的数据库加密。如果 table_encryption_privilege_check 系统变量已启用,则需要 TABLE_ENCRYPTION_ADMIN 权限才能指定与 default_table_encryption 设置不同的默认加密设置。更多信息,请参见定义模式和通用表空间的加密默认值。
MySQL 中的数据库实现为一个目录,其中包含与数据库中表对应的文件。由于数据库最初创建时不包含任何表,因此 CREATE
DATABASE 语句仅在 MySQL 数据目录下创建一个目录。允许的数据库名称规则在第 11.2
节“模式对象名称”中给出。如果数据库名称包含特殊字符,则数据库目录的名称包含这些字符的编码版本,如第 11.2.4
节“标识符到文件名的映射”所述。
在 MySQL 8.0 中,不支持通过手动在数据目录下创建目录(例如使用 mkdir)来创建数据库目录。
创建数据库时,请让服务器管理其目录及目录中的文件。直接操作数据库目录和文件可能导致不一致和意外结果。
MySQL 对数据库的数量没有限制。底层文件系统可能对目录数量有限制。
您也可以使用 mysqladmin 程序来创建数据库。参见第 6.5.2 节“mysqladmin — MySQL 服务器管理程序”。
DROP DATABASE
语法:
DROP {DATABASE | SCHEMA} [IF EXISTS] db_name
DROP DATABASE 删除数据库中的所有表并删除该数据库。使用此语句时需格外谨慎!要使用 DROP DATABASE,您需要对数据库拥有 DROP 权限。DROP SCHEMA 是 DROP DATABASE 的同义词。
重要提示
删除数据库时,专门为该数据库授予的权限不会自动删除。必须手动删除它们。参见第 15.7.1.6 节“GRANT 语句”。
IF EXISTS 用于防止在数据库不存在时发生错误。
如果删除了默认数据库,则默认数据库被取消设置(DATABASE() 函数返回 NULL)。
如果对符号链接数据库使用 DROP DATABASE,则链接和原始数据库都会被删除。
DROP DATABASE 返回被移除的表的数量。
DROP DATABASE 语句从给定的数据库目录中删除那些 MySQL 自身在正常操作期间可能创建的文件和目录。这包括具有以下列表所示扩展名的所有文件:
.BAK .DAT .HSH .MRG .MYD .MYI .cfg .db .ibd .ndb
如果在 MySQL 删除上述文件后,数据库目录中仍残留其他文件或目录,则无法删除该数据库目录。在这种情况下,您必须手动删除所有剩余的文件或目录,并再次发出 DROP DATABASE 语句。
删除数据库不会删除在该数据库中创建的任何 TEMPORARY 表。TEMPORARY 表会在创建它们的会话结束时自动删除。参见第 15.1.20.2 节“CREATE TEMPORARY TABLE 语句”。
您也可以使用 mysqladmin 删除数据库。参见第 6.5.2 节“mysqladmin — MySQL 服务器管理程序”。
ALTER DATABASE
语法:
ALTER {DATABASE | SCHEMA} [db_name]
alter_option ...
alter_option: {
[DEFAULT] CHARACTER SET [=] charset_name
| [DEFAULT] COLLATE [=] collation_name
| [DEFAULT] ENCRYPTION [=] {'Y' | 'N'}
| READ ONLY [=] {DEFAULT | 0 | 1}
}
ALTER DATABASE 使您能够更改数据库的整体特性。这些特性存储在数据字典中。此语句要求对数据库拥有 ALTER 权限。ALTER SCHEMA 是 ALTER DATABASE 的同义词。
如果省略数据库名,该语句将应用于默认数据库。在这种情况下,如果没有默认数据库,则会发生错误。
对于语句中省略的任何 alter_option,数据库将保留其当前的选项值,但更改字符集可能会更改排序规则,反之亦然。
- 字符集和排序规则选项
- 加密选项
- 只读选项
字符集和排序规则选项
CHARACTER SET 选项更改默认的数据库字符集。COLLATE 选项更改默认的数据库排序规则。有关字符集和排序规则名称的信息,请参见第 12 章“字符集、排序规则、Unicode”。
要查看可用的字符集和排序规则,请分别使用 SHOW CHARACTER SET 和 SHOW COLLATION 语句。参见第 15.7.7.3
节“SHOW CHARACTER SET 语句”和第 15.7.7.4 节“SHOW COLLATION 语句”。
创建时使用数据库默认设置的存储例程,会将这些默认设置作为其定义的一部分包含在内。(在存储例程中,如果未明确指定字符集或排序规则,则具有字符数据类型的变量会使用数据库默认设置。参见第
15.1.17 节“CREATE PROCEDURE 和 CREATE FUNCTION
语句”。)如果您更改数据库的默认字符集或排序规则,任何要使用新默认设置的存储例程都必须删除并重新创建。
加密选项
ENCRYPTION 选项(在 MySQL 8.0.16 中引入)定义默认的数据库加密设置,该设置由在该数据库中创建的表继承。允许的值为 ‘Y’(启用加密)和 ‘N’(禁用加密)。
mysql 系统模式不能设置为默认加密。其中的现有表是通用 mysql
表空间的一部分,该表空间可能是加密的。information_schema
仅包含视图。无法在其中创建任何表。磁盘上没有任何内容需要加密。performance_schema 中的所有表都使用
PERFORMANCE_SCHEMA 引擎,该引擎纯粹是内存中的。无法在其中创建任何其他表。磁盘上没有任何内容需要加密。
只有新创建的表继承默认的数据库加密。对于与数据库关联的现有表,其加密保持不变。如果
table_encryption_privilege_check 系统变量已启用,则需要 TABLE_ENCRYPTION_ADMIN
权限才能指定与 default_table_encryption
系统变量的值不同的默认加密设置。更多信息,请参见定义模式和通用表空间的加密默认值。
只读选项
READ ONLY 选项(在 MySQL 8.0.22 中引入)控制是否允许修改数据库及其中的对象。允许的值为 DEFAULT 或
0(非只读)和 1(只读)。此选项对于数据库迁移很有用,因为启用 READ ONLY 的数据库可以迁移到另一个 MySQL
实例,而无需担心数据库在操作期间可能被更改。
对于 NDB Cluster,在一个 mysqld 服务器上将数据库设置为只读会同步到同一集群中的其他 mysqld 服务器,从而使该数据库在所有 mysqld 服务器上都变为只读。
如果启用,READ ONLY 选项会显示在 INFORMATION_SCHEMA SCHEMATA_EXTENSIONS 表中。参见第 28.3.32 节“INFORMATION_SCHEMA SCHEMATA_EXTENSIONS 表”。
以下系统模式无法启用 READ ONLY 选项:mysql、information_schema、performance_schema。
在 ALTER DATABASE 语句中,READ ONLY 选项与自身的其他实例以及其他选项的交互方式如下:
- 如果 READ ONLY 的多个实例冲突(例如,READ READ >
- 即使对于只读数据库,也允许仅包含(非冲突的)READ ONLY 选项的 ALTER DATABASE 语句。
- 如果数据库在语句执行前或执行后的只读状态允许修改,则允许将(非冲突的)READ ONLY 选项与其他选项混合使用。如果数据库在语句执行前和执行后的只读状态都禁止更改,则会发生错误。
- 无论数据库是否只读,此语句都会成功:
ALTER DATABASE mydb READ ONLY = 0 DEFAULT COLLATE utf8mb4_bin;
- 如果数据库不是只读的,此语句会成功;但如果数据库已经是只读的,则会失败:
ALTER DATABASE mydb READ ONLY = 1 DEFAULT COLLATE utf8mb4_bin;
启用 READ ONLY 会影响数据库的所有用户,但以下例外情况不受只读检查的限制:
- 服务器作为服务器初始化、重启、升级或复制的一部分执行的语句。
- 在服务器启动时由 init_file 系统变量指定的文件中的语句。
- TEMPORARY 表;可以在只读数据库中创建、更改、删除 TEMPORARY 表并向其中写入数据。
- NDB Cluster 的非 SQL 插入和更新。
mysql> ALTER DATABASE cjc READ ONLY = 1; Query OK, 1 row affected (0.01 sec) mysql> insert into cjc.t2 values(5,'t',now()); ERROR 3989 (HY000): Schema 'cjc' is in read only mode. mysql> mysql> mysql> ALTER DATABASE cjc READ ONLY = 0; Query OK, 1 row affected (0.31 sec) mysql> insert into cjc.t2 values(5,'t',now()); Query OK, 1 row affected (0.01 sec)
除了上述例外操作外,启用 READ ONLY 会禁止对数据库及其对象(包括其定义、数据和元数据)进行写操作。以下列表详细说明了受影响的 SQL 语句和操作:
-
数据库本身:
- CREATE DATABASE
- ALTER DATABASE(更改 READ ONLY 选项除外)
- DROP DATABASE
-
视图:
- CREATE VIEW
- ALTER VIEW
- DROP VIEW
- 从调用具有副作用的函数的视图中选择。
- 更新可更新视图。
- 如果创建或删除可写数据库中的对象的语句会影响只读数据库中的视图的元数据(例如,使视图有效或无效),则这些语句会被拒绝。
-
存储例程:
- CREATE PROCEDURE
- DROP PROCEDURE
- CALL(调用具有副作用的存储过程)
- CREATE FUNCTION
- DROP FUNCTION
- SELECT(选择具有副作用的函数)
- 对于存储过程和函数,只读检查遵循预锁定行为。对于 CALL 语句,只读检查是基于每个语句进行的,因此,如果某个有条件执行的、向只读数据库写入的语句实际上并未执行,调用仍然成功。另一方面,对于在 SELECT 中调用的函数,函数体的执行发生在预锁定模式下。只要函数中的某个语句向只读数据库写入,无论该语句是否实际执行,函数的执行都会失败并报错。
-
触发器:
- CREATE TRIGGER
- DROP TRIGGER
- 触发器的调用。
-
事件:
- CREATE EVENT
- ALTER EVENT
- DROP EVENT
- 事件的执行:
- 在数据库中执行事件会失败,因为这会更改最后执行的时间戳,该时间戳是存储在数据字典中的事件元数据。事件执行的失败还会导致事件调度程序停止。
- 如果事件向只读数据库中的对象写入数据,则事件的执行会失败并报错,但事件调度程序不会停止。
-
表:
- CREATE TABLE
- ALTER TABLE
- CREATE INDEX
- DROP INDEX
- RENAME TABLE
- TRUNCATE TABLE
- DROP TABLE
- DELETE
- INSERT
- IMPORT TABLE
- LOAD DATA
- LOAD XML
- REPLACE
- UPDATE
- 对于子表位于只读数据库中的级联外键,即使子表未直接受影响,对父表的更新和删除也会被拒绝。
- 对于诸如
CREATE TABLE s1.t(i int) ENGINE MERGE UNION (s2.t, s3.t), INSERT_METHOD=...的 MERGE 表,适用以下行为:- 如果 s1、s2、s3 中至少有一个是只读的,则向 MERGE 表插入数据(
INSERT into s1.t)会失败,与插入方法无关。即使插入最终实际上会进入可写表,也会被拒绝。 - 只要 s1 不是只读的,删除 MERGE 表(
DROP TABLE s1.t)就会成功。允许删除引用只读数据库的 MERGE 表。
ALTER DATABASE 语句会阻塞,直到所有已访问被修改数据库中对象的并发事务提交为止。相反,访问在并发 ALTER DATABASE 中被修改的数据库中的对象的写事务会阻塞,直到 ALTER DATABASE 提交。
如果使用克隆插件克隆本地或远程数据目录,克隆中的数据库将保留其在源数据目录中的只读状态。只读状态不影响克隆过程本身。如果不希望在克隆中保持相同的数据库只读状态,必须在克隆过程完成后,使用 ALTER DATABASE 操作在克隆上显式更改该选项。
当从捐赠者克隆到接收者时,如果接收者有一个只读的用户数据库,克隆会失败并显示错误消息。在使数据库可写后,可以重试克隆。
READ ONLY 允许用于 ALTER DATABASE,但不允许用于 CREATE DATABASE。但是,对于只读数据库,SHOW CREATE DATABASE 生成的语句会在注释中包含 READ 以指示其只读状态:
- 如果 s1、s2、s3 中至少有一个是只读的,则向 MERGE 表插入数据(
mysql> ALTER DATABASE mydb READ ONLY = 1; mysql> SHOW CREATE DATABASE mydb\G *************************** 1. row *************************** Database: mydb Create Database: CREATE DATABASE `mydb` /*!40100 DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci */ /*!80016 DEFAULT ENCRYPTION='N' */ /* READ ONLY = 1 */
如果服务器执行包含此类注释的 CREATE DATABASE 语句,服务器会忽略注释,并且 READ ONLY 选项不会被处理。这对使用 SHOW CREATE DATABASE 在转储输出中生成 CREATE DATABASE 语句的 mysqldump 和 mysqlpump 有影响:
- 在转储文件中,只读数据库的 CREATE DATABASE 语句包含注释掉的 READ ONLY 选项。
- 转储文件可以像往常一样恢复,但由于服务器忽略注释掉的 READ ONLY 选项,恢复的数据库不是只读的。如果数据库在恢复后需要是只读的,必须手动执行 ALTER DATABASE 来使其只读。
假设 mydb 是只读的,并按如下方式转储:
$> mysqldump --databases mydb > mydb.sql
如果 mydb 在之后仍需保持只读,恢复操作后必须执行 ALTER DATABASE:
$> mysql mysql> SOURCE mydb.sql; mysql> ALTER DATABASE mydb READ ONLY = 1;
MySQL Enterprise Backup 不受此问题影响。它会像备份其他数据库一样备份和恢复只读数据库,但如果备份时启用了 READ ONLY 选项,则在恢复时会启用该选项。
ALTER DATABASE 会被写入二进制日志,因此复制源服务器上 READ ONLY
选项的更改也会影响副本。为了防止这种情况发生,必须在执行 ALTER DATABASE
语句之前禁用二进制日志记录。例如,要准备迁移数据库而不影响副本,请执行以下操作:
- 在单个会话中,禁用二进制日志记录并为数据库启用 READ ONLY:
mysql> SET sql_log_bin = OFF; mysql> ALTER DATABASE mydb READ ONLY = 1;
- 转储数据库,例如使用 mysqldump 或 mysqlpump:
$> mysqldump --databases mydb > mydb.sql
- 在单个会话中,禁用二进制日志记录并为数据库禁用 READ ONLY:
mysql> SET sql_log_bin = OFF; mysql> ALTER DATABASE mydb READ ONLY = 0;
03.DO语句
-
DO语句。
语法:
DO expr [, expr] ...
DO 执行表达式但不返回任何结果。在大多数情况下,DO 是 SELECT expr, … 的简写形式,但具有一个优势:当您不关心结果时,DO 的执行速度稍快。
DO 主要适用于具有副作用的函数,例如 RELEASE_LOCK()。
示例:此 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 (5.00 sec)
这在某些情况下很有用,例如在存储函数或触发器中,这些地方禁止产生结果集的语句。
DO 仅执行表达式。它不能用于所有可以使用 SELECT 的情况。例如,
DO id FROM t1 是无效的,因为它引用了表。
04.EXPLAIN SELECT语句
- 使用
EXPLAIN SELECT来获取查询优化器如何处理表的描述。
语法:
{EXPLAIN | DESCRIBE | DESC}
tbl_name [col_name | wild]
{EXPLAIN | DESCRIBE | DESC}
[explain_type]
{explainable_stmt | FOR CONNECTION connection_id}
{EXPLAIN | DESCRIBE | DESC} ANALYZE [explain_type] select_stmt
explain_type: {
FORMAT = format_name
}
format_name: {
TRADITIONAL
| JSON
| TREE
}
explainable_stmt: {
select_stmt
| TABLE ...
| DELETE ...
| INSERT ...
| REPLACE ...
| UPDATE ...
}
select_stmt:
SELECT ...
DESCRIBE 和 EXPLAIN 语句是同义词。在实际使用中,DESCRIBE 关键字更常用于获取表结构信息,而 EXPLAIN 用于获取查询执行计划(即解释 MySQL 将如何执行查询)。
以下讨论根据这些用途使用 DESCRIBE 和 EXPLAIN 关键字,但 MySQL 解析器将它们视为完全同义。
- 获取表结构信息
- 获取执行计划信息
- 使用 EXPLAIN ANALYZE 获取信息
获取表结构信息
DESCRIBE 提供有关表中列的信息:
mysql> desc cjc.t1; +-------+-------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +-------+-------------+------+-----+---------+-------+ | id | int | YES | | NULL | | | name | varchar(10) | YES | | NULL | | | time | time | YES | | NULL | | +-------+-------------+------+-----+---------+-------+ 3 rows in set (0.00 sec) mysql> explain cjc.t1; +-------+-------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +-------+-------------+------+-----+---------+-------+ | id | int | YES | | NULL | | | name | varchar(10) | YES | | NULL | | | time | time | YES | | NULL | | +-------+-------------+------+-----+---------+-------+ 3 rows in set (0.00 sec)
DESCRIBE 是 SHOW COLUMNS 的快捷方式。这些语句也显示视图的信息。SHOW COLUMNS 的描述提供了有关输出列的更多信息。参见第 15.7.7.5 节“SHOW COLUMNS 语句”。
默认情况下,DESCRIBE 显示表中所有列的信息。如果指定了
col_name,它是表中的列名。在这种情况下,该语句仅显示指定列的信息。如果指定了 wild,它是一个模式字符串。可以包含 SQL 通配符 %
和 _。在这种情况下,该语句仅显示名称与字符串匹配的列的输出。除非字符串包含空格或其他特殊字符,否则无需用引号括起来。
提供 DESCRIBE 语句是为了与 Oracle 兼容。
SHOW CREATE TABLE、SHOW TABLE STATUS 和 SHOW INDEX 语句也提供有关表的信息。参见第 15.7.7 节“SHOW 语句”。
MySQL 8.0.32 中添加的 explain_format 系统变量,在用于获取表列信息时,对 EXPLAIN 的输出没有影响。
获取执行计划信息
EXPLAIN 语句提供有关 MySQL 如何执行语句的信息:
- EXPLAIN 适用于 SELECT、DELETE、INSERT、REPLACE 和 UPDATE 语句。在 MySQL 8.0.19 及更高版本中,它也适用于 TABLE 语句。
- 当 EXPLAIN 与可解释的语句一起使用时,MySQL 显示优化器关于语句执行计划的信息。也就是说,MySQL 解释它将如何处理该语句,包括有关表如何连接以及以何种顺序连接的信息。有关使用 EXPLAIN 获取执行计划信息的信息,请参见第 10.8.2 节“EXPLAIN 输出格式”。
- 当 EXPLAIN 与 FOR CONNECTION connection_id 一起使用而不是与可解释的语句一起使用时,它显示指定连接中正在执行的语句的执行计划。参见第 10.8.4 节“获取命名连接的执行计划信息”。
- 对于可解释的语句,EXPLAIN 产生额外的执行计划信息,可以使用 SHOW WARNINGS 显示。参见第 10.8.3 节“扩展的 EXPLAIN 输出格式”。
- EXPLAIN 对于检查涉及分区表的查询很有用。参见第 26.3.5 节“获取有关分区的信息”。
- FORMAT 选项可用于选择输出格式。TRADITIONAL 以表格形式呈现输出。如果不存在 FORMAT 选项,这是默认格式。JSON 格式以 JSON 格式显示信息。在 MySQL 8.0.16 及更高版本中,TREE 提供树状输出,比 TRADITIONAL 格式更精确地描述查询处理;它是唯一显示哈希连接使用情况的格式(参见第 10.2.1.4 节“哈希连接优化”),并且始终用于 EXPLAIN ANALYZE。
- 从 MySQL 8.0.32 开始,EXPLAIN 使用的默认输出格式(即,当它没有 FORMAT 选项时)由 explain_format 系统变量的值决定。此变量的确切影响将在本节后面描述。
- 对于复杂的语句,JSON 输出可能相当大;特别是,阅读时很难配对右括号和左括号;为了使 JSON
结构的键(如果有)在右括号附近重复显示,设置 end_markers_in_json=ON。您应该注意,虽然这使输出更易于阅读,但也使 JSON
无效,导致 JSON 函数引发错误。
EXPLAIN 需要执行被解释语句所需的相同权限。此外,对于任何被解释的视图,EXPLAIN 还需要 SHOW VIEW 权限。如果指定的连接属于不同的用户,EXPLAIN … FOR CONNECTION 还需要 PROCESS 权限。
MySQL 8.0.32 引入的 explain_format 系统变量决定了当用于显示查询执行计划时 EXPLAIN 输出的格式。此变量可以接受与 FORMAT 选项一起使用的任何值,此外还包括作为 TRADITIONAL 同义词的 DEFAULT。以下示例使用可以从 MySQL:其他下载中获取的 world 数据库中的 country 表:
mysql> USE world; # 使 world 成为当前数据库
Database changed
检查 explain_format 的值,我们看到它具有默认值,因此 EXPLAIN(没有 FORMAT 选项)使用传统的表格输出:
mysql> SELECT @@explain_format; +------------------+ | @@explain_format | +------------------+ | TRADITIONAL | +------------------+ 1 row in set (0.00 sec) mysql> EXPLAIN SELECT Name FROM country WHERE Code Like 'A%'; +----+-------------+---------+------------+-------+---------------+---------+---------+------+------+----------+-------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+---------+------------+-------+---------------+---------+---------+------+------+----------+-------------+ | 1 | SIMPLE | country | NULL | range | PRIMARY | PRIMARY | 12 | NULL | 17 | 100.00 | Using where | +----+-------------+---------+------------+-------+---------------+---------+---------+------+------+----------+-------------+ 1 row in set, 1 warning (0.00 sec)
如果将 explain_format 的值设置为 TREE,然后重新运行相同的 EXPLAIN 语句,输出将使用树状格式:
mysql> SET @@explain_format=TREE;
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT @@explain_format;
+------------------+
| @@explain_format |
+------------------+
| TREE |
+------------------+
1 row in set (0.00 sec)
mysql> EXPLAIN SELECT Name FROM country WHERE Code LIKE 'A%';
+--------------------------------------------------------------------------------------------------------------+
| EXPLAIN |
+--------------------------------------------------------------------------------------------------------------+
| -> Filter: (country.`Code` like 'A%') (cost=3.67 rows=17)
-> Index range scan on country using PRIMARY over ('A' <= Code <= 'A????????') (cost=3.67 rows=17) |
+--------------------------------------------------------------------------------------------------------------+
1 row in set, 1 warning (0.00 sec)
如前所述,FORMAT 选项会覆盖此设置。使用 FORMAT=JSON 而不是 FORMAT=TREE 执行相同的 EXPLAIN 语句表明情况确实如此:
mysql> EXPLAIN FORMAT=JSON SELECT Name FROM country WHERE Code LIKE 'A%';
+------------------------------------------------------------------------------+
| EXPLAIN |
+------------------------------------------------------------------------------+
| {
"query_block": {
"select_id": 1,
"cost_info": {
"query_cost": "3.67"
},
"table": {
"table_name": "country",
"access_type": "range",
"possible_keys": [
"PRIMARY"
],
"key": "PRIMARY",
"used_key_parts": [
"Code"
],
"key_length": "12",
"rows_examined_per_scan": 17,
"rows_produced_per_join": 17,
"filtered": "100.00",
"cost_info": {
"read_cost": "1.97",
"eval_cost": "1.70",
"prefix_cost": "3.67",
"data_read_per_join": "16K"
},
"used_columns": [
"Code",
"Name"
],
"attached_condition": "(`world`.`country`.`Code` like 'A%')"
}
}
} |
+------------------------------------------------------------------------------+
1 row in set, 1 warning (0.00 sec)
要将 EXPLAIN 的默认输出恢复为表格格式,请将 explain_format 设置为 TRADITIONAL。或者,可以将其设置为 DEFAULT,这具有相同的效果,如下所示:
mysql> SET @@explain_format=DEFAULT; Query OK, 0 rows affected (0.00 sec) mysql> SELECT @@explain_format; +------------------+ | @@explain_format | +------------------+ | TRADITIONAL | +------------------+ 1 row in set (0.00 sec)
借助 EXPLAIN,您可以看到应该在表的哪些位置添加索引,以便通过使用索引查找行来加快语句执行速度。您还可以使用 EXPLAIN
来检查优化器是否以最佳顺序连接表。为了提示优化器使用与 SELECT 语句中表名顺序相对应的连接顺序,请以 SELECT
STRAIGHT_JOIN 而不是 SELECT 开始语句。(参见第 15.2.13 节“SELECT 语句”。)
优化器跟踪有时可能会提供与 EXPLAIN 互补的信息。但是,优化器跟踪的格式和内容可能因版本而异。有关详细信息,请参见第 10.15 节“跟踪优化器”。
如果您遇到索引未被使用而您认为应该使用的情况,请运行 ANALYZE TABLE 以更新表统计信息,例如键的基数,这可能会影响优化器做出的选择。参见第 15.7.3.1 节“ANALYZE TABLE 语句”。
注意
MySQL Workbench 具有 Visual Explain 功能,提供 EXPLAIN 输出的可视化表示。参见教程:使用 Explain 提高查询性能。
使用 EXPLAIN ANALYZE 获取信息
MySQL 8.0.18 引入了 EXPLAIN ANALYZE,它运行一条语句并生成 EXPLAIN 输出,以及计时和基于迭代器的额外信息,说明优化器的期望与实际执行的匹配情况。对于每个迭代器,提供以下信息:
- 估计的执行成本
(某些迭代器未计入成本模型,因此不包含在估计中。) - 估计的返回行数
- 返回第一行的时间
- 执行此迭代器所花费的时间(包括子迭代器,但不包括父迭代器),以毫秒为单位。
(当存在多个循环时,此数字显示每个循环的平均时间。) - 迭代器返回的行数
- 循环次数
查询执行信息使用 TREE 输出格式显示,其中节点表示迭代器。EXPLAIN ANALYZE 始终使用 TREE 输出格式。在 MySQL 8.0.21 及更高版本中,可以选择使用 FORMAT=TREE 明确指定;TREE 以外的格式仍然不受支持。
EXPLAIN ANALYZE 可以与 SELECT 语句以及多表 UPDATE 和 DELETE 语句一起使用。从 MySQL 8.0.19 开始,它也可以与 TABLE 语句一起使用。
从 MySQL 8.0.20 开始,您可以使用 KILL QUERY 或 CTRL-C 终止此语句。
EXPLAIN ANALYZE 不能与 FOR CONNECTION 一起使用。
示例输出:
mysql> EXPLAIN ANALYZE SELECT * FROM t1 JOIN t2 ON (t1.c1 = t2.c2)\G *************************** 1. row *************************** EXPLAIN: -> Inner hash join (t2.c2 = t1.c1) (cost=4.70 rows=6) (actual time=0.032..0.035 rows=6 loops=1) -> Table scan on t2 (cost=0.06 rows=6) (actual time=0.003..0.005 rows=6 loops=1) -> Hash -> Table scan on t1 (cost=0.85 rows=6) (actual time=0.018..0.022 rows=6 loops=1) mysql> EXPLAIN ANALYZE SELECT * FROM t3 WHERE i > 8\G *************************** 1. row *************************** EXPLAIN: -> Filter: (t3.i > 8) (cost=1.75 rows=5) (actual time=0.019..0.021 rows=6 loops=1) -> Table scan on t3 (cost=1.75 rows=15) (actual time=0.017..0.019 rows=15 loops=1) mysql> EXPLAIN ANALYZE SELECT * FROM t3 WHERE pk > 17\G *************************** 1. row *************************** EXPLAIN: -> Filter: (t3.pk > 17) (cost=1.26 rows=5) (actual time=0.013..0.016 rows=5 loops=1) -> Index range scan on t3 using PRIMARY (cost=1.26 rows=5) (actual time=0.012..0.014 rows=5 loops=1)
示例输出中使用的表由以下语句创建:
CREATE TABLE t1 ( c1 INTEGER DEFAULT NULL, c2 INTEGER DEFAULT NULL ); CREATE TABLE t2 ( c1 INTEGER DEFAULT NULL, c2 INTEGER DEFAULT NULL ); CREATE TABLE t3 ( pk INTEGER NOT NULL PRIMARY KEY, i INTEGER DEFAULT NULL );
此语句输出中显示的 actual time 值以毫秒表示。
从 MySQL 8.0.32 开始,explain_format 系统变量对 EXPLAIN ANALYZE 有以下影响:
- 如果此变量的值为 TRADITIONAL 或 TREE(或同义词 DEFAULT),EXPLAIN ANALYZE 使用 TREE 格式。这确保此语句在默认情况下继续使用 TREE 格式,就像在引入 explain_format 之前一样。
- 如果 explain_format 的值为 JSON,则 EXPLAIN ANALYZE 会返回错误,除非在语句中指定了 FORMAT=TREE。这是因为 EXPLAIN ANALYZE 仅支持 TREE 输出格式。
我们在下面说明第二点中描述的行为,复用上一个示例中的最后一个 EXPLAIN ANALYZE 语句:
mysql> SET @@explain_format=JSON; Query OK, 0 rows affected (0.00 sec) mysql> SELECT @@explain_format; +------------------+ | @@explain_format | +------------------+ | JSON | +------------------+ 1 row in set (0.00 sec) mysql> EXPLAIN ANALYZE SELECT * FROM t3 WHERE pk > 17\G ERROR 1235 (42000): This version of MySQL doesn't yet support 'EXPLAIN ANALYZE with JSON format' mysql> EXPLAIN ANALYZE FORMAT=TRADITIONAL SELECT * FROM t3 WHERE pk > 17\G ERROR 1235 (42000): This version of MySQL doesn't yet support 'EXPLAIN ANALYZE with TRADITIONAL format' mysql> EXPLAIN ANALYZE FORMAT=TREE SELECT * FROM t3 WHERE pk > 17\G *************************** 1. row *************************** EXPLAIN: -> Filter: (t3.pk > 17) (cost=1.26 rows=5) (actual time=0.013..0.016 rows=5 loops=1) -> Index range scan on t3 using PRIMARY (cost=1.26 rows=5) (actual time=0.012..0.014 rows=5 loops=1)
无论 explain_format 的值如何,将 FORMAT=TRADITIONAL 或 FORMAT=JSON 与 EXPLAIN ANALYZE 一起使用总会引发错误。
从 MySQL 8.0.33 开始,EXPLAIN ANALYZE 和 EXPLAIN FORMAT=TREE 输出中的数字根据以下规则格式化:
- 范围在 0.001-999999.5 之间的数字打印为十进制数字。
- 小于 1000 的十进制数字具有三位有效数字;其余的有四位、五位或六位。
- 范围在 0.001-999999.5 之外的数字以工程格式打印。此类值的示例有 1.23e+9 和 934e-6。
- 不打印尾随零。例如,我们打印 2.3 而不是 2.30,打印 1.2e+6 而不是 1.20e+6。
- 小于 1e-12 的数字打印为 0。
05.FLUSH和RESET语句
-
FLUSH和RESET语句。
FLUSH
语法:
FLUSH [NO_WRITE_TO_BINLOG | LOCAL] {
flush_option [, flush_option] ...
| tables_option
}
flush_option: {
BINARY LOGS
| ENGINE LOGS
| ERROR LOGS
| GENERAL LOGS
| HOSTS
| LOGS
| PRIVILEGES
| OPTIMIZER_COSTS
| RELAY LOGS [FOR CHANNEL channel]
| SLOW LOGS
| STATUS
| USER_RESOURCES
}
tables_option: {
table_synonym
| table_synonym tbl_name [, tbl_name] ...
| table_synonym WITH READ LOCK
| table_synonym tbl_name [, tbl_name] ... WITH READ LOCK
| table_synonym tbl_name [, tbl_name] ... FOR EXPORT
}
table_synonym: {
TABLE
| TABLES
}
FLUSH 语句有几种变体形式,用于清除或重新加载各种内部缓存、刷新表或获取锁。每个 FLUSH 操作都需要其描述中指示的权限。
注意
无法在存储函数或触发器中发出 FLUSH 语句。但是,可以在存储过程中使用 FLUSH,只要这些存储过程不是从存储函数或触发器中调用的。参见第 27.8 节“存储程序的限制”。
默认情况下,服务器将 FLUSH 语句写入二进制日志,以便它们复制到副本。要禁止记录日志,请指定可选的 NO_WRITE_TO_BINLOG 关键字或其别名 LOCAL。
注意
在任何情况下,FLUSH LOGS、FLUSH BINARY LOGS、FLUSH TABLES WITH READ
LOCK(无论是否带有表列表)和 FLUSH TABLES tbl_name … FOR EXPORT
都不会写入二进制日志,因为如果复制到副本会导致问题。
FLUSH 语句会导致隐式提交。参见第 15.3.3 节“导致隐式提交的语句”。
mysqladmin 实用程序通过 flush-hosts、flush-logs、flush-privileges、flush-status 和
flush-tables 等命令提供了一些刷新操作的命令行界面。参见第 6.5.2 节“mysqladmin — MySQL
服务器管理程序”。
向服务器发送 SIGHUP 或 SIGUSR1 信号会导致发生几次刷新操作,类似于各种形式的 FLUSH 语句。信号可以由 root
系统账户或拥有服务器进程的系统账户发送。这使得无需连接到服务器即可执行刷新操作,而连接服务器需要具有足够权限的 MySQL 账户。参见第
6.10 节“MySQL 中的 Unix 信号处理”。
RESET 语句类似于 FLUSH。有关将 RESET 与复制一起使用的信息,请参见第 15.7.8.6 节“RESET 语句”。
以下列表描述了允许的 FLUSH 语句 flush_option 值。有关允许的 tables_option 值的描述,请参见 FLUSH TABLES 语法。
-
FLUSH BINARY LOGS
关闭并重新打开服务器正在写入的任何二进制日志文件。如果启用了二进制日志记录,二进制日志文件的序列号将相对于前一个文件递增一。
此操作需要 RELOAD 权限。 -
FLUSH ENGINE LOGS
关闭并重新打开已安装存储引擎的任何可刷新的日志。这会导致 InnoDB 将其日志刷新到磁盘。
此操作需要 RELOAD 权限。 -
FLUSH ERROR LOGS
关闭并重新打开服务器正在写入的任何错误日志文件。
此操作需要 RELOAD 权限。 -
FLUSH GENERAL LOGS
关闭并重新打开服务器正在写入的任何通用查询日志文件。
此操作需要 RELOAD 权限。
此操作对用于通用查询日志的表没有影响(参见第 7.4.1 节“选择通用查询日志和慢查询日志输出目标”)。 -
FLUSH HOSTS
清空主机缓存和公开缓存内容的 Performance Schema host_cache 表,并解除对任何被阻止主机的阻止。
此操作需要 RELOAD 权限。
有关为什么刷新主机缓存可能是可取或必要的信息,请参见第 7.1.12.3 节“DNS 查找和主机缓存”。
注意
从 MySQL 8.0.23 开始,FLUSH HOSTS 已弃用;预计在未来的 MySQL 版本中会将其移除。取而代之的是,截断 Performance Schema host_cache 表:
TRUNCATE TABLE performance_schema.host_cache;
TRUNCATE TABLE 操作需要对表的 DROP 权限,而不是 RELOAD 权限。请注意,TRUNCATE TABLE 语句不会写入二进制日志。要从 FLUSH HOSTS 获得相同行为,请在语句中指定 NO_WRITE_TO_BINLOG 或 LOCAL。 -
FLUSH LOGS
关闭并重新打开服务器正在写入的任何日志文件。
此操作需要 RELOAD 权限。
此操作的效果相当于以下操作的综合效果:
FLUSH BINARY LOGS
FLUSH ENGINE LOGS
FLUSH ERROR LOGS
FLUSH GENERAL LOGS
FLUSH RELAY LOGS
FLUSH SLOW LOGS -
FLUSH OPTIMIZER_COSTS
重新读取成本模型表,以便优化器开始使用其中存储的当前成本估算。
此操作需要 FLUSH_OPTIMIZER_COSTS 或 RELOAD 权限。
服务器会为任何无法识别的成本模型表条目向错误日志写入警告。有关这些表的信息,请参见第 10.9.5 节“优化器成本模型”。此操作仅影响在刷新之后开始的会话。现有会话继续使用它们开始时当前的成本估算。 -
FLUSH PRIVILEGES
从 mysql 系统模式中的授权表重新读取权限。作为此操作的一部分,服务器读取包含动态权限分配的 global_grants 表,并注册在那里找到的任何未注册的权限。
只有当您直接对授权表进行更改时,重新加载授权表对于使 MySQL 权限和用户的更新生效才是必要的;对于诸如 GRANT 或 REVOKE 这样的账户管理语句则不需要,这些语句会立即生效。更多信息,请参见第 8.2.13 节“权限更改何时生效”。
此操作需要 RELOAD 权限。
如果在服务器启动时指定了 --skip-grant-tables 选项来禁用 MySQL 权限系统,FLUSH PRIVILEGES 提供了一种在运行时启用权限系统的方法。
重置失败登录跟踪(如果服务器是以 --skip-grant-tables 启动的,则启用它)并解锁任何临时锁定的账户。参见第 8.2.15 节“密码管理”。
释放服务器因 GRANT、CREATE USER、CREATE SERVER 和 INSTALL PLUGIN 语句而缓存的内存。相应的 REVOKE、DROP USER、DROP SERVER 和 UNINSTALL PLUGIN 语句不会释放此内存,因此对于执行了许多导致缓存的语句实例的服务器,除非使用 FLUSH PRIVILEGES 释放,否则缓存内存使用量会增加。
清除 caching_sha2_password 认证插件使用的内存缓存。参见 SHA-2 可插拔认证的缓存操作。 -
FLUSH RELAY LOGS [FOR CHANNEL channel]
关闭并重新打开服务器正在写入的任何中继日志文件。如果启用了中继日志记录,中继日志文件的序列号将相对于前一个文件递增一。
此操作需要 RELOAD 权限。
FOR CHANNEL channel 子句允许您指定操作适用于哪个复制通道。执行FLUSH RELAY LOGS FOR CHANNEL channel可刷新特定复制通道的中继日志。如果未指定通道且不存在额外的复制通道,则操作应用于默认通道。如果未指定通道且存在多个复制通道,则操作应用于所有复制通道。更多信息,请参见第 19.2.2 节“复制通道”。 -
FLUSH SLOW LOGS
关闭并重新打开服务器正在写入的任何慢查询日志文件。
此操作需要 RELOAD 权限。
此操作对用于慢查询日志的表没有影响(参见第 7.4.1 节“选择通用查询日志和慢查询日志输出目标”)。 -
FLUSH STATUS
刷新状态指示器。
此操作将当前线程的会话状态变量值添加到全局值,并将会话值重置为零。一些全局变量也可能被重置为零。它还将键缓存(默认和命名的)的计数器重置为零,并将 Max_used_connections 设置为当前打开的连接数。这些信息在调试查询时可能有用。参见第 1.5 节“如何报告错误或问题”。
FLUSH STATUS 不受 read_only 或 super_read_only 的影响,并且总是写入二进制日志。
此操作需要 FLUSH_STATUS 或 RELOAD 权限。 -
FLUSH USER_RESOURCES
将所有每小时用户资源指示器重置为零。
此操作需要 FLUSH_USER_RESOURCES 或 RELOAD 权限。
重置资源指示器使达到每小时连接、查询或更新限制的客户端能够立即恢复活动。FLUSH USER_RESOURCES 不适用于由 max_user_connections 系统变量控制的最大同时连接数限制。参见第 8.2.21 节“设置账户资源限制”。
FLUSH TABLES 语法
FLUSH TABLES 刷新表,并根据使用的变体获取锁。FLUSH 语句中使用的任何 TABLES 变体必须是唯一使用的选项。FLUSH TABLE 是 FLUSH TABLES 的同义词。
注意
此处指示通过关闭表来刷新表的描述对于 InnoDB 有所不同,InnoDB 将表内容刷新到磁盘但保持表打开。只要其他活动不修改表,这仍然允许在表打开时复制表文件。
-
FLUSH TABLES
关闭所有打开的表,强制关闭所有正在使用的表,并刷新预处理语句缓存。
此操作需要 FLUSH_TABLES 或 RELOAD 权限。
有关预处理语句缓存的信息,请参见第 10.10.3 节“预处理语句和存储程序的缓存”。
当存在活跃的 LOCK TABLES … READ 时,不允许使用 FLUSH TABLES。要刷新和锁定表,请改用FLUSH TABLES tbl_name ... WITH READ LOCK。 -
FLUSH TABLES tbl_name [, tbl_name] …
使用一个或多个逗号分隔的表名列表,此操作类似于没有名称的 FLUSH TABLES,但服务器仅刷新指定的表。如果指定的表不存在,不会发生错误。
此操作需要 FLUSH_TABLES 或 RELOAD 权限。 -
FLUSH TABLES WITH READ LOCK
关闭所有打开的表,并使用全局读锁锁定所有数据库的所有表。
此操作需要 FLUSH_TABLES 或 RELOAD 权限。
如果您有像 Veritas 或 ZFS 这样可以及时拍摄快照的文件系统,此操作是获取备份的非常方便的方法。使用 UNLOCK TABLES 来释放锁。
FLUSH TABLES WITH READ LOCK 获取的是全局读锁而不是表锁,因此在表锁定和隐式提交方面,其行为与 LOCK TABLES 和 UNLOCK TABLES 不同:- 仅当当前有任何表已使用 LOCK TABLES 锁定时,UNLOCK TABLES 才会隐式提交任何活跃事务。对于在 FLUSH TABLES WITH READ LOCK 之后的 UNLOCK TABLES,不会发生提交,因为后一条语句不获取表锁。
- 开始事务会导致使用 LOCK TABLES 获取的表锁被释放,就像您执行了 UNLOCK TABLES 一样。开始事务不会释放使用 FLUSH TABLES WITH READ LOCK 获取的全局读锁。
FLUSH TABLES WITH READ LOCK 不会阻止服务器向日志表中插入行(参见第 7.4.1 节“选择通用查询日志和慢查询日志输出目标”)。
-
FLUSH TABLES tbl_name [, tbl_name] … WITH READ LOCK
刷新并为指定的表获取读锁。
此操作需要 FLUSH_TABLES 或 RELOAD 权限。由于它获取表锁,因此还需要对每个表拥有 LOCK TABLES 权限。
该操作首先获取表的独占元数据锁,因此它会等待打开这些表的事务完成。然后,该操作从表缓存中刷新表,重新打开表,获取表锁(类似于 LOCK TABLES … READ),并将元数据锁从独占降级为共享。在操作获取锁并降级元数据锁之后,其他会话可以读取但不能修改表。
此操作仅适用于现有的基表(非 TEMPORARY 表)。如果名称引用基表,则使用该表。如果引用 TEMPORARY 表,则忽略它。如果名称应用于视图,则会发生 ER_WRONG_OBJECT 错误。否则,会发生 ER_NO_SUCH_TABLE 错误。
使用 UNLOCK TABLES 来释放锁,使用 LOCK TABLES 来释放锁并获取其他锁,或使用 START TRANSACTION 来释放锁并开始新事务。
这种 FLUSH TABLES 变体允许在单个操作中刷新和锁定表。它提供了一种变通方法,用于解决当存在活跃的 LOCK TABLES … READ 时不允许使用 FLUSH TABLES 的限制。
此操作不会执行隐式的 UNLOCK TABLES,因此,如果在有任何活跃的 LOCK TABLES 时执行此操作,或者在没有首先释放已获取的锁的情况下再次使用它,则会导致错误。
如果使用 HANDLER 打开了被刷新的表,则处理程序会被隐式刷新并丢失其位置。 -
FLUSH TABLES tbl_name [, tbl_name] … FOR EXPORT
这种 FLUSH TABLES 变体适用于 InnoDB 表。它确保对命名表的更改已刷新到磁盘,以便在服务器运行时可以进行二进制表复制。
此操作需要 FLUSH_TABLES 或 RELOAD 权限。由于它为了准备导出表而获取表锁,因此还需要对每个表拥有 LOCK TABLES 和 SELECT 权限。
该操作的工作方式如下:- 它获取命名表的共享元数据锁。只要其他会话有修改了这些表的活跃事务或持有这些表的表锁,操作就会阻塞。当锁被获取后,操作会阻止尝试更新表的事务,同时允许只读操作继续。
- 它检查所有表的存储引擎是否都支持 FOR EXPORT。如果有任何不支持,则会发生 ER_ILLEGAL_HA 错误并且操作失败。
- 操作通知每个表的存储引擎使表准备好导出。存储引擎必须确保任何挂起的更改都已写入磁盘。
- 操作将会话置于锁表模式,以便 FOR EXPORT 操作完成后不会释放先前获取的元数据锁。
此操作仅适用于现有的基表(非 TEMPORARY 表)。如果名称引用基表,则使用该表。如果引用 TEMPORARY 表,则忽略它。如果名称应用于视图,则会发生 ER_WRONG_OBJECT 错误。否则,会发生 ER_NO_SUCH_TABLE 错误。
InnoDB 支持对拥有自己 .ibd 文件的表使用 FOR EXPORT(即,在启用 innodb_file_per_table 设置的情况下创建的表)。InnoDB 在收到 FOR EXPORT 操作的通知时确保任何更改都已刷新到磁盘。这允许在 FOR EXPORT 操作生效期间对表内容进行二进制复制,因为 .ibd 文件是事务一致的,并且可以在服务器运行时复制。FOR EXPORT 不适用于 InnoDB 系统表空间文件,也不适用于具有 FULLTEXT 索引的 InnoDB 表。
FLUSH TABLES … FOR EXPORT 支持分区的 InnoDB 表。
当收到 FOR EXPORT 通知时,InnoDB 会将通常保存在内存中或表空间文件外单独磁盘缓冲区中的某些类型的数据写入磁盘。对于每个表,InnoDB 还会在与表相同的数据库目录中生成一个名为 table_name.cfg 的文件。.cfg 文件包含稍后重新导入表空间文件(到相同或不同的服务器)所需的元数据。
当 FOR EXPORT 操作完成时,InnoDB 已将所有脏页刷新到表数据文件。任何更改缓冲区条目都在刷新之前合并。此时,表被锁定并处于静默状态:表在磁盘上处于事务一致的状态,您可以复制 .ibd 表空间文件以及相应的 .cfg 文件以获取这些表的一致快照。
关于将复制的表数据重新导入到 MySQL 实例的过程,请参见第 17.6.1.3 节“导入 InnoDB 表”。
当您处理完表后,使用 UNLOCK TABLES 来释放锁,使用 LOCK TABLES 来释放锁并获取其他锁,或使用 START TRANSACTION 来释放锁并开始新事务。
当会话中任何这些语句生效时,尝试使用 FLUSH TABLES … FOR EXPORT 会产生错误:
FLUSH TABLES … WITH READ LOCK
FLUSH TABLES … FOR EXPORT
LOCK TABLES … READ
LOCK TABLES … WRITE
当会话中 FLUSH TABLES … FOR EXPORT 生效时,尝试使用以下任何语句都会产生错误:
FLUSH TABLES WITH READ LOCK
FLUSH TABLES … WITH READ LOCK
FLUSH TABLES … FOR EXPORT
RESET
语法:
RESET reset_option [, reset_option] ...
reset_option: {
MASTER
| REPLICA
| SLAVE
}
RESET 语句用于清除各种服务器操作的状态。您必须具有 RELOAD 权限才能执行 RESET。
有关移除持久化全局系统变量的 RESET PERSIST 语句的信息,请参见第 15.7.8.7 节“RESET PERSIST 语句”。
RESET 相当于 FLUSH 语句的增强版本。参见第 15.7.8.3 节“FLUSH 语句”。
RESET 语句会导致隐式提交。参见第 15.3.3 节“导致隐式提交的语句”。
以下列表描述了允许的 RESET 语句 reset_option 值:
-
RESET MASTER
删除索引文件中列出的所有二进制日志,将二进制日志索引文件重置为空,并创建一个新的二进制日志文件。 -
RESET REPLICA
使副本忘记其在源二进制日志中的复制位置。还会通过删除任何现有的中继日志文件并开始一个新的中继日志来重置中继日志。从 MySQL 8.0.22 开始,使用 RESET REPLICA 代替 RESET SLAVE。
06.SET语句
-
SET语句。
语法:
SET variable = expr [, variable = expr] ...
variable: {
user_var_name
| param_name
| local_var_name
| {GLOBAL | @@GLOBAL.} system_var_name
| {PERSIST | @@PERSIST.} system_var_name
| {PERSIST_ONLY | @@PERSIST_ONLY.} system_var_name
| [SESSION | @@SESSION. | @@] system_var_name
}
SET 语法用于变量赋值,使您能够为影响服务器或客户端操作的不同类型变量赋值:
- 用户定义变量。参见第 11.4 节“用户定义变量”。
- 存储过程和函数参数,以及存储程序局部变量。参见第 15.6.4 节“存储程序中的变量”。
- 系统变量。参见第 7.1.8 节“服务器系统变量”。系统变量也可以在服务器启动时设置,如第 7.1.9 节“使用系统变量”所述。
赋值变量值的 SET 语句不会被写入二进制日志,因此在复制场景中,它仅影响执行该语句的主机。要影响所有复制主机,需在每个主机上执行该语句。
以下部分描述了设置变量的 SET 语法。它们使用
= 赋值运算符,但也可以使用
:= 赋值运算符。
- 用户定义变量赋值
- 参数和局部变量赋值
- 系统变量赋值
- SET 错误处理
- 多变量赋值
- 表达式中的系统变量引用
用户定义变量赋值
用户定义变量在会话内局部创建,仅存在于该会话的上下文中;参见第 11.4 节“用户定义变量”。
用户定义变量写作
@var_name,并分配一个表达式值,如下所示:
SET @var_name = expr;
示例:
SET @name = 43; SET @total_tax = (SELECT SUM(tax) FROM taxable_transactions);
如这些语句所示,expr 的范围可以从简单(字面值)到更复杂(标量子查询返回的值)。
Performance Schema 中的 user_variables_by_thread 表包含有关用户定义变量的信息。参见第 29.12.10 节“Performance Schema 用户定义变量表”。
参数和局部变量赋值
SET 适用于定义它们的存储对象上下文中的参数和局部变量。以下过程使用 increment 过程参数和 counter 局部变量:
CREATE PROCEDURE p(increment INT) BEGIN DECLARE counter INT DEFAULT 0; WHILE counter < 10 DO -- ... 执行工作 ... SET counter = counter + increment; END WHILE; END;
系统变量赋值
MySQL
服务器维护配置其操作的系统变量。系统变量可以具有影响整个服务器操作的全局值,影响当前会话的会话值,或两者兼有。许多系统变量是动态的,可以在运行时使用
SET 语句更改以影响当前服务器实例的操作。SET 也可用于将某些系统变量持久化到数据目录中的 mysqld-auto.cnf
文件,以影响后续启动的服务器操作。
如果为敏感系统变量发出 SET 语句,则在将查询记录到通用日志和审计日志之前,会重写查询以将值替换为
。即使服务器实例上无法通过密钥环组件进行安全存储,也会发生这种情况。
如果您更改会话系统变量,该值将在您的会话中生效,直到您将变量更改为其他值或会话结束。该更改对其他会话没有影响。
如果您更改全局系统变量,该值将被记住并用于初始化新会话的会话值,直到您将变量更改为其他值或服务器退出。任何访问全局值的客户端都可以看到该更改。但是,该更改仅对更改后连接的客户端影响相应的会话值。全局变量更改不会影响任何当前客户端会话的会话值(甚至不会影响发生全局值更改的会话)。
要使全局系统变量设置永 久生效,以便在服务器重启后仍然适用,您可以将其持久化到数据目录中的 mysqld-auto.cnf
文件。也可以通过手动修改 my.cnf
选项文件来进行持久配置更改,但那样更麻烦,并且手动输入设置中的错误可能要到很晚才会被发现。持久化系统变量的 SET
语句更方便,并且避免了格式错误的设置的可能性,因为语法错误的设置不会成功,也不会更改服务器配置。有关持久化系统变量和
mysqld-auto.cnf 文件的更多信息,请参见第 7.1.9.3 节“持久化系统变量”。
注意
设置或持久化全局系统变量值通常需要特殊权限。设置会话系统变量值通常不需要特殊权限,任何用户都可以执行,但也有例外。有关更多信息,请参见第 7.1.9.1 节“系统变量权限”。
以下讨论描述了设置和持久化系统变量的语法选项:
- 要为全局系统变量赋值,请在变量名称前加上 GLOBAL 关键字或
@@GLOBAL.限定符:
SET GLOBAL max_connections = 1000; SET @@GLOBAL.max_connections = 1000;
- 要为会话系统变量赋值,请在变量名称前加上 SESSION 或 LOCAL 关键字,或
@@SESSION.、@@LOCAL.或@@限定符,或者不使用任何关键字或修饰符:
SET SESSION sql_mode = 'TRADITIONAL'; SET LOCAL sql_mode = 'TRADITIONAL'; SET @@SESSION.sql_mode = 'TRADITIONAL'; SET @@LOCAL.sql_mode = 'TRADITIONAL'; SET @@sql_mode = 'TRADITIONAL'; SET sql_mode = 'TRADITIONAL';
客户端可以更改自己的会话变量,但不能更改任何其他客户端的会话变量。
- 要将全局系统变量持久化到数据目录中的 mysqld-auto.cnf 选项文件,请在变量名称前加上 PERSIST 关键字或
@@PERSIST.限定符:
SET PERSIST max_connections = 1000; SET @@PERSIST.max_connections = 1000;
此 SET 语法允许您在运行时进行配置更改,这些更改在服务器重启后仍然存在。与 SET GLOBAL 类似,SET PERSIST 设置全局变量的运行时值,但还将变量设置写入 mysqld-auto.cnf 文件(替换任何现有的变量设置,如果有的话)。
- 要将全局系统变量持久化到 mysqld-auto.cnf 文件而不设置全局变量的运行时值,请在变量名称前加上 PERSIST_ONLY 关键字或
@@PERSIST_ONLY.限定符:
SET PERSIST_ONLY back_log = 100; SET @@PERSIST_ONLY.back_log = 100;
与 PERSIST 类似,PERSIST_ONLY 将变量设置写入 mysqld-auto.cnf。但是,与 PERSIST 不同,PERSIST_ONLY 不修改全局变量的运行时值。这使得 PERSIST_ONLY 适用于配置只能在服务器启动时设置的只读系统变量。
要将全局系统变量值设置为编译时的 MySQL 默认值,或将会话系统变量设置为当前相应的全局值,请将变量设置为值 DEFAULT。例如,以下两个语句在将 max_join_size 的会话值设置为当前全局值方面是相同的:
SET @@SESSION.max_join_size = DEFAULT; SET @@SESSION.max_join_size = @@GLOBAL.max_join_size;
使用 SET 将全局系统变量持久化为 DEFAULT 或其字面默认值,会将变量设置为其默认值,并将变量设置添加到 mysqld-auto.cnf。要从文件中删除该变量,请使用 RESET PERSIST。
有些系统变量无法持久化或持久化受限。参见第 7.1.9.4 节“不可持久化和持久化受限的系统变量”。
由插件实现的系统变量可以在执行 SET
语句时安装插件的情况下持久化。如果插件仍然安装,则持久化的插件变量赋值将在后续服务器重启时生效。如果插件不再安装,则服务器读取
mysqld-auto.cnf 文件时插件变量不再存在。在这种情况下,服务器将警告写入错误日志并继续:
currently unknown variable ‘var_name’
was read from the persisted config file
要显示系统变量名称和值:
- 使用 SHOW VARIABLES 语句;参见第 15.7.7.41 节“SHOW VARIABLES 语句”。
- 几个 Performance Schema 表提供系统变量信息。参见第 29.12.14 节“Performance Schema 系统变量表”。
- Performance Schema variables_info 表包含显示每个系统变量最近设置时间和用户的信息。参见第 29.12.14.2 节“Performance Schema variables_info 表”。
- Performance Schema persisted_variables 表为 mysqld-auto.cnf 文件提供 SQL 接口,允许在运行时使用 SELECT 语句检查其内容。参见第 29.12.14.1 节“Performance Schema persisted_variables 表”。
SET 错误处理
如果 SET 语句中的任何变量赋值失败,则整个语句失败,不会更改任何变量,也不会更改 mysqld-auto.cnf 文件。
在以下描述的情况下,SET 会产生错误。大多数示例显示使用关键字语法(例如 GLOBAL 或 SESSION)的 SET 语句,但这些原则也适用于使用相应修饰符(例如
@@GLOBAL. 或
@@SESSION.)的语句。
- 使用 SET(任何变体)设置只读变量:
mysql> SET GLOBAL version = 'abc'; ERROR 1238 (HY000): Variable 'version' is a read only variable
- 使用 GLOBAL、PERSIST 或 PERSIST_ONLY 设置仅具有会话值的变量:
mysql> SET GLOBAL sql_log_bin = ON; ERROR 1228 (HY000): Variable 'sql_log_bin' is a SESSION variable and can't be used with SET GLOBAL
- 使用 SESSION 设置仅具有全局值的变量:
mysql> SET SESSION max_connections = 1000; ERROR 1229 (HY000): Variable 'max_connections' is a GLOBAL variable and should be set with SET GLOBAL
-
省略 GLOBAL、PERSIST 或 PERSIST_ONLY 来设置仅具有全局值的变量:
mysql> SET max_connections = 1000;
ERROR 1229 (HY000): Variable ‘max_connections’ is a GLOBAL variable and should be set with SET GLOBAL -
使用 PERSIST 或 PERSIST_ONLY 设置无法持久化的变量:
mysql> SET PERSIST port = 3307; ERROR 1238 (HY000): Variable 'port' is a read only variable mysql> SET PERSIST_ONLY port = 3307; ERROR 1238 (HY000): Variable 'port' is a non persistent read only variable
-
@@GLOBAL.、@@PERSIST.、@@PERSIST_ONLY.、@@SESSION.和@@修饰符仅适用于系统变量。尝试将它们应用于用户定义变量、存储过程或函数参数或存储程序局部变量会导致错误。 - 并非所有系统变量都可以设置为 DEFAULT。在这种情况下,分配 DEFAULT 会导致错误。
- 尝试将 DEFAULT 分配给用户定义变量、存储过程或函数参数或存储程序局部变量会导致错误。
多变量赋值
一个 SET 语句可以包含多个变量赋值,以逗号分隔。此语句为用户定义变量和系统变量赋值:
SET @x = 1, SESSION sql_mode = ‘’;
如果在单个语句中设置多个系统变量,则语句中最近的 GLOBAL、PERSIST、PERSIST_ONLY 或 SESSION 关键字将用于后续未指定关键字的赋值。
多变量赋值示例:
SET GLOBAL sort_buffer_size = 1000000, SESSION sort_buffer_size = 1000000; SET @@GLOBAL.sort_buffer_size = 1000000, @@LOCAL.sort_buffer_size = 1000000; SET GLOBAL max_connections = 1000, sort_buffer_size = 1000000;
@@GLOBAL.、
@@PERSIST.、
@@PERSIST_ONLY.、
@@SESSION. 和
@@ 修饰符仅适用于紧随其后的系统变量,而不适用于任何剩余的系统变量。此语句将 sort_buffer_size 的全局值设置为 50000,会话值设置为 1000000:
SET @@GLOBAL.sort_buffer_size = 50000, sort_buffer_size = 1000000;
表达式中的系统变量引用
要在表达式中引用系统变量的值,请使用 @@ 修饰符之一(除了
@@PERSIST. 和
@@PERSIST_ONLY.,它们不允许在表达式中使用)。例如,您可以在 SELECT 语句中检索系统变量值,如下所示:
SELECT @@GLOBAL.sql_mode, @@SESSION.sql_mode, @@sql_mode;
注意
在表达式中引用系统变量为
@@var_name(使用
@@ 而不是
@@GLOBAL. 或
@@SESSION.)时,如果存在会话值则返回会话值,否则返回全局值。这与
SET @@var_name = expr 不同,后者始终引用会话值。
07.SHOW语句
-
SHOW语句。参见第 15.7.7 节,“SHOW 语句”。许多 MySQL 特定的SHOW语句所产生的信息,可以通过使用SELECT查询INFORMATION_SCHEMA以更标准的方式获得。参见第 28 章,INFORMATION_SCHEMA 表。
SHOW 语句有多种形式,可提供有关数据库、表、列或服务器状态信息。
如果特定 SHOW 语句的语法包含
LIKE 'pattern' 部分,则
'pattern' 是一个可以包含 SQL 通配符
% 和
_ 的字符串。该模式可用于将语句输出限制为匹配的值。
多个 SHOW 语句也接受 WHERE 子句,这为指定要显示的行提供了更大的灵活性。参见第 28.8 节“SHOW 语句的扩展”。
在 SHOW 语句的结果中,用户名和主机名使用反引号(`)进行引用。
许多 MySQL API(例如 PHP)允许您像处理 SELECT 语句的结果集一样处理从 SHOW 语句返回的结果;更多信息,请参见第 31
章“连接器和 API”或您的 API 文档。此外,您可以在 SQL 中处理对 INFORMATION_SCHEMA
数据库中的表进行查询的结果,而这对于 SHOW 语句的结果则不那么容易实现。参见第 28 章“INFORMATION_SCHEMA 表”。
语法:
SHOW {BINARY | MASTER} LOGS
SHOW BINLOG EVENTS [IN 'log_name'] [FROM pos] [LIMIT [offset,] row_count]
SHOW {CHARACTER SET | CHARSET} [like_or_where]
SHOW COLLATION [like_or_where]
SHOW [FULL] COLUMNS FROM tbl_name [FROM db_name] [like_or_where]
SHOW CREATE DATABASE db_name
SHOW CREATE EVENT event_name
SHOW CREATE FUNCTION func_name
SHOW CREATE PROCEDURE proc_name
SHOW CREATE TABLE tbl_name
SHOW CREATE TRIGGER trigger_name
SHOW CREATE VIEW view_name
SHOW DATABASES [like_or_where]
SHOW ENGINE engine_name {STATUS | MUTEX}
SHOW [STORAGE] ENGINES
SHOW ERRORS [LIMIT [offset,] row_count]
SHOW EVENTS
SHOW FUNCTION CODE func_name
SHOW FUNCTION STATUS [like_or_where]
SHOW GRANTS FOR user
SHOW INDEX FROM tbl_name [FROM db_name]
SHOW MASTER STATUS
SHOW OPEN TABLES [FROM db_name] [like_or_where]
SHOW PLUGINS
SHOW PROCEDURE CODE proc_name
SHOW PROCEDURE STATUS [like_or_where]
SHOW PRIVILEGES
SHOW [FULL] PROCESSLIST
SHOW PROFILE [types] [FOR QUERY n] [OFFSET n] [LIMIT n]
SHOW PROFILES
SHOW RELAYLOG EVENTS [IN 'log_name'] [FROM pos] [LIMIT [offset,] row_count]
SHOW {REPLICAS | SLAVE HOSTS}
SHOW {REPLICA | SLAVE} STATUS [FOR CHANNEL channel]
SHOW [GLOBAL | SESSION] STATUS [like_or_where]
SHOW TABLE STATUS [FROM db_name] [like_or_where]
SHOW [FULL] TABLES [FROM db_name] [like_or_where]
SHOW TRIGGERS [FROM db_name] [like_or_where]
SHOW [GLOBAL | SESSION] VARIABLES [like_or_where]
SHOW WARNINGS [LIMIT [offset,] row_count]
like_or_where: {
LIKE 'pattern'
| WHERE expr
}
常用命令:
SHOW BINARY LOGS Statement SHOW BINLOG EVENTS Statement SHOW CHARACTER SET Statement SHOW COLLATION Statement SHOW COLUMNS Statement SHOW CREATE DATABASE Statement SHOW CREATE EVENT Statement SHOW CREATE FUNCTION Statement SHOW CREATE PROCEDURE Statement SHOW CREATE TABLE Statement SHOW CREATE TRIGGER Statement SHOW CREATE USER Statement SHOW CREATE VIEW Statement SHOW DATABASES Statement SHOW ENGINE Statement SHOW ENGINES Statement SHOW ERRORS Statement SHOW EVENTS Statement SHOW FUNCTION CODE Statement SHOW FUNCTION STATUS Statement SHOW GRANTS Statement SHOW INDEX Statement SHOW MASTER STATUS Statement SHOW OPEN TABLES Statement SHOW PLUGINS Statement SHOW PRIVILEGES Statement SHOW PROCEDURE CODE Statement SHOW PROCEDURE STATUS Statement SHOW PROCESSLIST Statement SHOW PROFILE Statement SHOW PROFILES Statement SHOW RELAYLOG EVENTS Statement SHOW REPLICAS Statement SHOW SLAVE HOSTS | SHOW REPLICAS Statement SHOW REPLICA STATUS Statement SHOW SLAVE | REPLICA STATUS Statement SHOW STATUS Statement SHOW TABLE STATUS Statement SHOW TABLES Statement SHOW TRIGGERS Statement SHOW VARIABLES Statement SHOW WARNINGS Statement
08.LOAD DATA语句
- 使用
LOAD DATA。在许多情况下,此语法与 Oracle 的LOAD DATA兼容。参见第 15.2.9 节,“LOAD DATA 语句”。
语法:
LOAD DATA
[LOW_PRIORITY | CONCURRENT] [LOCAL]
INFILE 'file_name'
[REPLACE | IGNORE]
INTO TABLE tbl_name
[PARTITION (partition_name [, partition_name] ...)]
[CHARACTER SET charset_name]
[{FIELDS | COLUMNS}
[TERMINATED BY 'string']
[[OPTIONALLY] ENCLOSED BY 'char']
[ESCAPED BY 'char']
]
[LINES
[STARTING BY 'string']
[TERMINATED BY 'string']
]
[IGNORE number {LINES | ROWS}]
[(col_name_or_user_var
[, col_name_or_user_var] ...)]
[SET col_name={expr | DEFAULT}
[, col_name={expr | DEFAULT}] ...]
LOAD DATA 语句以极高的速度从文本文件中读取数据行并加载到表中。文件可以从服务器主机或客户端主机读取,具体取决于是否指定了 LOCAL 修饰符。LOCAL 也会影响数据解释和错误处理。
LOAD DATA 是 SELECT … INTO OUTFILE 的补充操作。(参见第 15.2.13.1 节“SELECT … INTO
语句”。)要将表中的数据写入文件,请使用 SELECT … INTO OUTFILE。要将文件读回表中,请使用 LOAD DATA。两个语句的
FIELDS 和 LINES 子句的语法是相同的。
mysqlimport 实用程序提供了另一种加载数据文件的方式;它通过向服务器发送 LOAD DATA 语句来操作。参见第 6.5.5 节“mysqlimport — 数据导入程序”。
有关 INSERT 与 LOAD DATA 的效率以及加速 LOAD DATA 的信息,请参见第 10.2.5.1 节“优化 INSERT 语句”。
- 非 LOCAL 与 LOCAL 操作
- 输入文件字符集
- 输入文件位置
- 安全要求
- 重复键和错误处理
- 索引处理
- 字段和行处理
- 列列表指定
- 输入预处理
- 列值赋值
- 分区表支持
- 并发考虑
- 语句结果信息
- 复制考虑
- 其他主题
非 LOCAL 与 LOCAL 操作
与非 LOCAL 操作相比,LOCAL 修饰符影响 LOAD DATA 的以下方面:
- 它改变了输入文件的预期位置;参见输入文件位置。
- 它改变了语句的安全要求;参见安全要求。
- 除非也指定了 REPLACE,否则 LOCAL 对输入文件内容的解释和错误处理具有与 IGNORE 修饰符相同的效果;参见重复键和错误处理,以及列值赋值。
LOCAL 仅当服务器和您的客户端都已配置为允许时才有效。例如,如果 mysqld 启动时禁用了 local_infile 系统变量,LOCAL 会产生错误。参见第 8.1.6 节“LOAD DATA LOCAL 的安全考虑”。
输入文件字符集
文件名必须指定为字符串字面量。在 Windows 上,路径名中的反斜杠应指定为正斜杠或双反斜杠。服务器使用 character_set_filesystem 系统变量指示的字符集解释文件名。
默认情况下,服务器使用 character_set_database 系统变量指示的字符集解释文件内容。如果文件内容使用的字符集与此默认值不同,最好使用 CHARACTER SET 子句指定该字符集。字符集为 binary 表示“不进行转换”。
SET NAMES 和 character_set_client 的设置不影响文件内容的解释。
LOAD DATA 将文件中的所有字段解释为具有相同的字符集,无论字段值加载到的列的数据类型如何。为了正确解释文件,您必须确保文件是以正确的字符集编写的。例如,如果您使用
mysqldump -T 或在 mysql 中执行 SELECT … INTO OUTFILE 语句来编写数据文件,请务必使用
--default-character-set 选项以在文件通过 LOAD DATA 加载时使用的字符集编写输出。
注意
无法加载使用 ucs2、utf16、utf16le 或 utf32 字符集的数据文件。
输入文件位置
以下规则决定 LOAD DATA 输入文件的位置:
- 如果未指定 LOCAL,文件必须位于服务器主机上。服务器直接读取文件,定位方式如下:
- 如果文件名是绝对路径名,服务器按给定的路径使用。
- 如果文件名是带有前导组件的相对路径名,服务器相对于其数据目录查找文件。
- 如果文件名没有前导组件,服务器在默认数据库的数据库目录中查找文件。
- 如果指定了 LOCAL,文件必须位于客户端主机上。客户端程序读取文件,定位方式如下:
- 如果文件名是绝对路径名,客户端程序按给定的路径使用。
- 如果文件名是相对路径名,客户端程序相对于其调用目录查找文件。
- 当使用 LOCAL 时,客户端程序读取文件并将其内容发送到服务器。服务器在其存储临时文件的目录中创建文件的副本。参见第 B.3.3.5 节“MySQL 存储临时文件的位置”。此目录中副本空间不足可能导致 LOAD DATA LOCAL 语句失败。
非 LOCAL 规则意味着服务器读取相对于其数据目录的名为
./myfile.txt 的文件,而从默认数据库的数据库目录中读取名为
myfile.txt 的文件。例如,如果在 db1 是默认数据库时执行以下 LOAD DATA 语句,服务器会从 db1 的数据库目录中读取
data.txt 文件,即使该语句明确将文件加载到 db2 数据库的表中:
LOAD DATA INFILE 'data.txt' INTO TABLE db2.my_table;
注意
服务器也使用非 LOCAL 规则来定位 IMPORT TABLE 语句的 .sdi 文件。
安全要求
对于非 LOCAL 加载操作,服务器读取位于服务器主机上的文本文件,因此必须满足以下安全要求:
- 您必须拥有 FILE 权限。参见第 8.2.2 节“MySQL 提供的权限”。
- 该操作受 secure_file_priv 系统变量设置的限制:
- 如果变量值是非空目录名,则文件必须位于该目录中。
- 如果变量值为空(这不安全),则文件只需要服务器可读即可。
对于 LOCAL 加载操作,客户端程序读取位于客户端主机上的文本文件。因为文件内容由客户端通过连接发送到服务器,所以使用 LOCAL 比服务器直接访问文件稍慢。另一方面,您不需要 FILE 权限,并且文件可以位于客户端程序可以访问的任何目录中。
重复键和错误处理
REPLACE 和 IGNORE 修饰符控制处理在唯一键值(PRIMARY KEY 或 UNIQUE 索引值)上与现有表行重复的新(输入)行:
- 使用 REPLACE,具有与现有行中唯一键值相同值的新行会替换现有行。参见第 15.2.12 节“REPLACE 语句”。
- 使用 IGNORE,在唯一键值上重复现有行的新行将被丢弃。更多信息,请参见 IGNORE 对语句执行的影响。
LOCAL 修饰符具有与 IGNORE 相同的效果。这是因为服务器无法在操作中途停止文件的传输。
如果未指定 REPLACE、IGNORE 或 LOCAL 中的任何一个,当发现重复键值时会发生错误,并且文本文件的其余部分将被忽略。
除了如上所述影响重复键处理外,IGNORE 和 LOCAL 还影响错误处理:
- 当未指定 IGNORE 或 LOCAL 时,数据解释错误会终止操作。
- 当指定 IGNORE — 或未指定 REPLACE 的 LOCAL — 时,数据解释错误会变为警告,并且加载操作会继续,即使 SQL 模式是限制性的。有关示例,请参见列值赋值。
索引处理
要在加载操作期间忽略外键约束,请在执行 LOAD DATA 之前执行
SET foreign_key_checks = 0 语句。
如果在空的 MyISAM 表上使用 LOAD DATA,所有非唯一索引都会在一个单独的批次中创建(类似于 REPAIR TABLE)。通常,当您有许多索引时,这会使 LOAD DATA 快得多。在某些极端情况下,您可以通过在将文件加载到表之前使用
ALTER TABLE ... DISABLE KEYS 禁用索引,并在加载文件后使用
ALTER TABLE ... ENABLE KEYS 重新创建索引,来更快地创建索引。参见第 10.2.5.1 节“优化 INSERT 语句”。
字段和行处理
对于 LOAD DATA 和 SELECT … INTO OUTFILE 语句,FIELDS 和 LINES 子句的语法是相同的。两个子句都是可选的,但如果两者都指定,FIELDS 必须位于 LINES 之前。
如果指定了 FIELDS 子句,其每个子句(TERMINATED BY、[OPTIONALLY] ENCLOSED BY 和 ESCAPED BY)也是可选的,但您必须至少指定其中一个。这些子句的参数只能包含 ASCII 字符。
如果未指定 FIELDS 或 LINES 子句,则默认值与编写以下内容相同:
FIELDS TERMINATED BY '\t' ENCLOSED BY '' ESCAPED BY '\\' LINES TERMINATED BY '\n' STARTING BY ''
反斜杠是 SQL 语句中字符串内的 MySQL 转义字符。因此,要指定一个字面量反斜杠,您必须指定两个反斜杠,该值才会被解释为单个反斜杠。转义序列
\t 和
\n 分别指定制表符和换行符。
换句话说,默认值导致 LOAD DATA 在读取输入时执行以下操作:
- 在换行符处查找行边界。
- 不跳过任何行前缀。
- 在制表符处将行拆分为字段。
- 不期望字段被任何引号字符括起来。
- 将转义字符
\之前的字符解释为转义序列。例如,\t、\n和\\分别表示制表符、换行符和反斜杠。有关完整的转义序列列表,请参见后面关于 FIELDS ESCAPED BY 的讨论。
相反,默认值导致 SELECT … INTO OUTFILE 在写入输出时执行以下操作:
- 在字段之间写入制表符。
- 不用任何引号字符括起字段。
- 使用
\转义字段值中出现的制表符、换行符或\。 - 在行尾写入换行符。
注意
对于在 Windows 系统上生成的文本文件,正确的文件读取可能需要
LINES TERMINATED BY '\r\n',因为 Windows 程序通常使用两个字符作为行终止符。某些程序(如 WordPad)在写入文件时可能使用
\r 作为行终止符。要读取此类文件,请使用
LINES TERMINATED BY '\r'。
如果所有输入行都有一个您想忽略的公共前缀,您可以使用
LINES STARTING BY 'prefix_string' 来跳过该前缀及其之前的任何内容。如果某行不包含该前缀,则整行将被跳过。假设您发出以下语句:
LOAD DATA INFILE '/tmp/test.txt' INTO TABLE test FIELDS TERMINATED BY ',' LINES STARTING BY 'xxx';
如果数据文件如下所示:
xxx"abc",1 something xxx"def",2 "ghi",3
则结果行为 (“abc”,1) 和 (“def”,2)。文件中的第三行被跳过,因为它不包含前缀。
IGNORE number LINES 子句可用于忽略文件开头的行。例如,您可以使用
IGNORE 1 LINES 来跳过包含列名的初始标题行:
LOAD DATA INFILE '/tmp/test.txt' INTO TABLE test IGNORE 1 LINES;
当您将 SELECT … INTO OUTFILE 与 LOAD DATA 结合使用时,以将数据从数据库写入文件,然后再将文件读回数据库,两个语句的字段和行处理选项必须匹配。否则,LOAD DATA 将无法正确解释文件内容。假设您使用 SELECT … INTO OUTFILE 写入一个字段以逗号分隔的文件:
SELECT * INTO OUTFILE 'data.txt' FIELDS TERMINATED BY ',' FROM table2;
要读取以逗号分隔的文件,正确的语句是:
LOAD DATA INFILE 'data.txt' INTO TABLE table2 FIELDS TERMINATED BY ',';
如果您尝试使用下面显示的语句读取该文件,它将不起作用,因为它指示 LOAD DATA 在字段之间查找制表符:
LOAD DATA INFILE 'data.txt' INTO TABLE table2 FIELDS TERMINATED BY '\t';
可能的结果是每个输入行将被解释为单个字段。
LOAD DATA 可用于读取从外部源获得的文件。例如,许多程序可以以逗号分隔值 (CSV)
格式导出数据,这样行中的字段以逗号分隔并用双引号括起来,并带有初始的列名行。如果此类文件中的行以回车/换行对终止,则此处显示的语句说明了您将用于加载文件的字段和行处理选项:
LOAD DATA INFILE 'data.txt' INTO TABLE tbl_name FIELDS TERMINATED BY ',' ENCLOSED BY '"' LINES TERMINATED BY '\r\n' IGNORE 1 LINES;
如果输入值不一定括在引号内,请在 ENCLOSED BY 选项前使用 OPTIONALLY。
任何字段或行处理选项都可以指定一个空字符串 (
'')。如果不为空,FIELDS [OPTIONALLY]
ENCLOSED BY 和 FIELDS ESCAPED BY 的值必须是单个字符。FIELDS TERMINATED BY、LINES
STARTING BY 和 LINES TERMINATED BY
的值可以是多个字符。例如,要写入以回车/换行对终止的行,或读取包含此类行的文件,请指定
LINES TERMINATED BY '\r\n' 子句。
要读取一个包含笑话的文件,笑话由
%% 组成的行分隔,您可以这样做:
CREATE TABLE jokes (a INT NOT NULL AUTO_INCREMENT PRIMARY KEY, joke TEXT NOT NULL); LOAD DATA INFILE '/tmp/jokes.txt' INTO TABLE jokes FIELDS TERMINATED BY '' LINES TERMINATED BY '\n%%\n' (joke);
FIELDS [OPTIONALLY] ENCLOSED BY 控制字段的引号。对于输出 (SELECT … INTO OUTFILE),如果省略 OPTIONALLY 一词,所有字段都由 ENCLOSED BY 字符括起来。这种输出的示例如下所示(使用逗号作为字段分隔符):
"1","a string","100.20" "2","a string containing a , comma","102.20" "3","a string containing a \" quote","102.20" "4","a string containing a \", quote and comma","102.20"
如果指定 OPTIONALLY,则 ENCLOSED BY 字符仅用于括起具有字符串数据类型(如 CHAR、BINARY、TEXT 或 ENUM)的列的值:
1,"a string",100.20 2,"a string containing a , comma",102.20 3,"a string containing a \" quote",102.20 4,"a string containing a \", quote and comma",102.20
字段值中出现的 ENCLOSED BY 字符通过在其前面加上 ESCAPED BY 字符进行转义。此外,如果指定了空的 ESCAPED BY 值,可能会无意中生成 LOAD DATA 无法正确读取的输出。例如,如果转义字符为空,则前面显示的输出将如下所示。请注意,第四行中的第二个字段在引号后面包含一个逗号,这(错误地)看起来终止了字段:
1,"a string",100.20 2,"a string containing a , comma",102.20 3,"a string containing a " quote",102.20 4,"a string containing a ", quote and comma",102.20
对于输入,ENCLOSED BY 字符(如果存在)会从字段值的两端剥离。(无论是否指定了
OPTIONALLY,这都是正确的;OPTIONALLY 对输入解释没有影响。)由 ESCAPED BY 字符引导的 ENCLOSED BY
字符的出现被解释为当前字段值的一部分。
如果字段以 ENCLOSED BY 字符开头,则仅当其后跟字段或行的 TERMINATED BY 序列时,该字符的实例才被识别为终止字段值。为了避免歧义,字段值中出现的 ENCLOSED BY 字符可以加倍,并解释为该字符的单个实例。例如,如果指定了
ENCLOSED BY '"',则引号的处理方式如下所示:
"The ""BIG"" boss" -> The "BIG" boss The "BIG" boss -> The "BIG" boss The ""BIG"" boss -> The ""BIG"" boss
FIELDS ESCAPED BY 控制如何读取或写入特殊字符:
-
对于输入,如果 FIELDS ESCAPED BY 字符不为空,则去除该字符的出现,并将后续字符按字面意义作为字段值的一部分。有一些双字符序列是例外,其中第一个字符是转义字符。这些序列如下表所示(使用
\作为转义字符)。NULL 处理的规则将在本节后面描述。有关 -转义语法的更多信息,请参见第 11.1.1 节“字符串字面量”。
如果 FIELDS ESCAPED BY 字符为空,则不进行转义序列解释。 -
对于输出,如果 FIELDS ESCAPED BY 字符不为空,则用于在输出中为以下字符添加前缀:
- FIELDS ESCAPED BY 字符。
- FIELDS [OPTIONALLY] ENCLOSED BY 字符。
- FIELDS TERMINATED BY 和 LINES TERMINATED BY 值的第一个字符,如果 ENCLOSED BY 字符为空或未指定。
- ASCII 0(实际写入转义字符后的是 ASCII 0,而不是零值字节)。
如果 FIELDS ESCAPED BY 字符为空,则不转义任何字符,并且 NULL 输出为 NULL,而不是 \N。指定空的转义字符可能不是一个好主意,特别是当您的数据中的字段值包含刚才列出的任何字符时。
在某些情况下,字段和行处理选项会相互影响:
- 如果 LINES TERMINATED BY 是空字符串且 FIELDS TERMINATED BY 非空,则行也以 FIELDS TERMINATED BY 终止。
- 如果 FIELDS TERMINATED BY 和 FIELDS ENCLOSED BY 值都为空 (
''),则使用固定行(无分隔符)格式。在固定行格式中,字段之间不使用分隔符(但您仍然可以有行终止符)。相反,列值使用足够宽的字段宽度进行读取和写入,以容纳字段中的所有值。对于 TINYINT、SMALLINT、MEDIUMINT、INT 和 BIGINT,字段宽度分别为 4、6、8、11 和 20,无论声明的显示宽度如何。
LINES TERMINATED BY 仍用于分隔行。如果某行不包含所有字段,则其余列将设置为其默认值。如果您没有行终止符,应将其设置为''。在这种情况下,文本文件必须包含每一行的所有字段。
固定行格式也会影响 NULL 值的处理,如后面所述。
注意
如果您使用多字节字符集,固定大小格式将不起作用。
NULL 值的处理根据使用的 FIELDS 和 LINES 选项而有所不同:
- 对于默认的 FIELDS 和 LINES 值,NULL 在输出时作为字段值
\N写入,而字段值\N在输入时作为 NULL 读取(假设 ESCAPED BY 字符是\)。 - 如果 FIELDS ENCLOSED BY 不为空,则包含字面单词 NULL 作为其值的字段将作为 NULL 值读取。这与括在 FIELDS ENCLOSED BY 字符内的单词 NULL 不同,后者作为字符串
'NULL'读取。 - 如果 FIELDS ESCAPED BY 为空,则 NULL 写为单词 NULL。
- 对于固定行格式(当 FIELDS TERMINATED BY 和 FIELDS ENCLOSED BY 都为空时使用),NULL
写为空字符串。这导致表中的 NULL
值和空字符串在写入文件时无法区分,因为两者都写为空字符串。如果您需要在读回文件时能够区分两者,则不应使用固定行格式。
尝试将 NULL 加载到 NOT NULL 列中会根据列值赋值中描述的规则产生警告或错误。
LOAD DATA 不支持某些情况:
- 固定大小的行(FIELDS TERMINATED BY 和 FIELDS ENCLOSED BY 都为空)和 BLOB 或 TEXT 列。
- 如果您指定的一个分隔符与另一个分隔符相同或是其前缀,LOAD DATA 无法正确解释输入。例如,以下 FIELDS 子句会导致问题:
FIELDS TERMINATED BY ‘"’ ENCLOSED BY ‘"’ - 如果 FIELDS ESCAPED BY 为空,则包含 FIELDS ENCLOSED BY 或 LINES TERMINATED BY 的出现,后跟 FIELDS TERMINATED BY 值的字段值会导致 LOAD DATA 过早停止读取字段或行。这是因为 LOAD DATA 无法正确确定字段或行值的结束位置。
列列表指定
以下示例加载 persondata 表的所有列:
LOAD DATA INFILE 'persondata.txt' INTO TABLE persondata;
默认情况下,当 LOAD DATA 语句末尾未提供列列表时,期望输入行包含每个表列的一个字段。如果您只想加载表的部分列,请指定一个列列表:
LOAD DATA INFILE 'persondata.txt' INTO TABLE persondata (col_name_or_user_var [, col_name_or_user_var] ...);
如果输入文件中字段的顺序与表中列的顺序不同,您也必须指定列列表。否则,MySQL 无法确定如何将输入字段与表列匹配。
输入预处理
LOAD DATA 语法中的每个 col_name_or_user_var 实例要么是列名,要么是用户变量。对于用户变量,SET 子句允许您在对结果分配给列之前对其值执行预处理转换。
SET 子句中的用户变量可以以多种方式使用。以下示例直接将第一个输入列用于 t1.column1 的值,并将第二个输入列分配给一个用户变量,该变量在用于 t1.column2 的值之前经过除法运算:
LOAD DATA INFILE 'file.txt' INTO TABLE t1 (column1, @var1) SET column2 = @var1/100;
SET 子句可用于提供不从输入文件派生的值。以下语句将 column3 设置为当前日期和时间:
LOAD DATA INFILE 'file.txt' INTO TABLE t1 (column1, column2) SET column3 = CURRENT_TIMESTAMP;
您还可以通过将输入值分配给用户变量而不将该变量分配给任何表列来丢弃输入值:
LOAD DATA INFILE 'file.txt' INTO TABLE t1 (column1, @dummy, column2, @dummy, column3);
列/变量列表和 SET 子句的使用受以下限制:
- SET 子句中的赋值应在赋值运算符的左侧只有列名。
- 您可以在 SET 赋值的右侧使用子查询。要分配给列的子查询必须是标量子查询。此外,您不能使用子查询从正在加载的表中进行选择。
- 被
IGNORE number LINES子句忽略的行不会为列/变量列表或 SET 子句进行处理。 - 当使用固定行格式加载数据时,不能使用用户变量,因为用户变量没有显示宽度。
列值赋值
为了处理输入行,LOAD DATA 将其拆分为字段,并根据列/变量列表和 SET 子句(如果存在)使用这些值。然后将结果行插入到表中。如果表有 BEFORE INSERT 或 AFTER INSERT 触发器,它们分别在插入行之前或之后被激活。
字段值的解释和表列的赋值取决于以下因素:
- SQL 模式(sql_mode 系统变量的值)。模式可以是非限制性的,或者以各种方式具有限制性。例如,可以启用严格 SQL 模式,或者模式可以包含 NO_ZERO_DATE 或 NO_ZERO_IN_DATE 等值。
- 存在或不存在 IGNORE 和 LOCAL 修饰符。
这些因素结合起来,导致 LOAD DATA 产生限制性或非限制性的数据解释: - 如果 SQL 模式是限制性的并且未指定 IGNORE 或 LOCAL 修饰符,则数据解释是限制性的。错误会终止加载操作。
- 如果 SQL 模式是非限制性的或者指定了 IGNORE 或 LOCAL 修饰符,则数据解释是非限制性的。(特别是,如果指定了任一修饰符且省略了 REPLACE 修饰符,则会覆盖限制性的 SQL 模式。)错误会变为警告,并且加载操作会继续。
限制性数据解释使用以下规则:
- 字段过多或过少会导致错误。
- 将 NULL(即
\N)分配给非 NULL 列会导致错误。 - 超出列数据类型范围的值会导致错误。
- 无效值会产生错误。例如,数值列的
'x'值会导致错误,而不是转换为 0。
相比之下,非限制性数据解释使用以下规则:
- 如果输入行字段过多,则忽略多余的字段,并增加警告数量。
- 如果输入行字段过少,则缺少输入字段的列将分配其默认值。默认值赋值在第 13.6 节“数据类型默认值”中描述。
- 将 NULL(即
\N)分配给非 NULL 列会导致分配该列数据类型的隐式默认值。隐式默认值在第 13.6 节“数据类型默认值”中描述。 - 无效值会产生警告而不是错误,并转换为列数据类型的“最接近”的有效值。示例:
- 数值列的
'x'值会转换为 0。 - 超出范围的数值或时间值会被剪裁到列数据类型范围的最接近端点。
- DATETIME、DATE 或 TIME 列的无效值会作为隐式默认值插入,无论 SQL 模式 NO_ZERO_DATE 设置如何。隐式默认值是该类型的适当“零”值(
'0000-00-00 00:00:00'、'0000-00-00'或'00:00:00')。参见第 13.2 节“日期和时间数据类型”。
- 数值列的
- LOAD DATA 对空字段值与缺失字段的处理方式不同:
- 对于字符串类型,列设置为空字符串。
- 对于数值类型,列设置为 0。
- 对于日期和时间类型,列设置为该类型的适当“零”值。参见第 13.2 节“日期和时间数据类型”。
- 这些值与您在 INSERT 或 UPDATE 语句中显式将空字符串分配给字符串、数值或日期或时间类型时产生的值相同。
TIMESTAMP 列仅在列值为 NULL(即
\N)且该列未声明为允许 NULL 值,或者 TIMESTAMP 列的默认值是当前时间戳并且在指定字段列表时从字段列表中省略时,才设置为当前日期和时间。
LOAD DATA 将所有输入视为字符串,因此您不能像使用 INSERT 语句那样对 ENUM 或 SET 列使用数值。所有 ENUM 和 SET 值必须指定为字符串。
BIT 值不能直接使用二进制表示法加载(例如,
b'011010')。要解决此问题,请使用 SET 子句去除前导的
b' 和尾随的
',并执行从二进制到十进制的转换,以便 MySQL 正确地将值加载到 BIT 列中:
$> cat /tmp/bit_test.txt b'10' b'1111111' $> mysql test mysql> LOAD DATA INFILE '/tmp/bit_test.txt' INTO TABLE bit_test (@var1) SET b = CAST(CONV(MID(@var1, 3, LENGTH(@var1)-3), 2, 10) AS UNSIGNED); Query OK, 2 rows affected (0.00 sec) Records: 2 Deleted: 0 Skipped: 0 Warnings: 0 mysql> SELECT BIN(b+0) FROM bit_test; +----------+ | BIN(b+0) | +----------+ | 10 | | 1111111 | +----------+ 2 rows in set (0.00 sec)
对于使用 0b 二进制表示法的 BIT 值(例如,
0b011010),请改用此 SET 子句去除前导的
0b:
SET b = CAST(CONV(MID(@var1, 3, LENGTH(@var1)-2), 2, 10) AS UNSIGNED)
分区表支持
LOAD DATA 支持使用 PARTITION 子句进行显式分区选择,该子句包含一个或多个逗号分隔的分区、子分区或两者的名称列表。当使用此子句时,如果文件中的任何行无法插入到列表中指定的任何分区或子分区中,则语句将失败并显示错误
Found a row not matching the given partition set。有关更多信息和示例,请参见第 26.5 节“分区选择”。
并发考虑
使用 LOW_PRIORITY 修饰符,LOAD DATA 语句的执行将延迟到没有其他客户端从表中读取数据为止。这仅影响使用表级锁定的存储引擎(如 MyISAM、MEMORY 和 MERGE)。
使用 CONCURRENT 修饰符和满足并发插入条件(即中间没有空闲块)的 MyISAM 表,其他线程可以在 LOAD DATA 执行时从表中检索数据。此修饰符会影响 LOAD DATA 的性能,即使没有其他线程同时使用该表。
语句结果信息
当 LOAD DATA 语句完成时,它返回以下格式的信息字符串:
Records: 1 Deleted: 0 Skipped: 0 Warnings: 0
警告出现在与使用 INSERT 语句插入值相同的情况下(参见第 15.2.7 节“INSERT 语句”),除了当输入行中字段过少或过多时,LOAD DATA 也会生成警告。
您可以使用 SHOW WARNINGS 获取前 max_error_count 个警告的列表,以了解出了什么问题。参见第 15.7.7.42 节“SHOW WARNINGS 语句”。
如果您使用 C API,可以通过调用 mysql_info() 函数获取有关语句的信息。参见 mysql_info()。
复制考虑
LOAD DATA 对于基于语句的复制被认为是不可靠的。如果您在 binlog_format=STATEMENT 的情况下使用 LOAD
DATA,每个要应用更改的副本都会创建一个包含数据的临时文件。即使源上的二进制日志加密处于活动状态,此临时文件也不会被加密。如果需要加密,请改用基于行或混合的二进制日志记录格式,这样副本就不会创建临时文件。有关
LOAD DATA 与复制之间交互的更多信息,请参见第 19.5.1.19 节“复制和 LOAD DATA”。
其他主题
在 Unix 上,如果您需要 LOAD DATA 从管道读取,可以使用以下技术(该示例将
/ 目录的列表加载到表 db1.t1 中):
mkfifo /mysql/data/db1/ls.dat chmod 666 /mysql/data/db1/ls.dat find / -ls > /mysql/data/db1/ls.dat & mysql -e "LOAD DATA INFILE 'ls.dat' INTO TABLE t1" db1
在这里,您必须在不同的终端上运行生成要加载数据的命令和 mysql 命令,或者在后台运行数据生成过程(如前面的示例所示)。如果不这样做,管道将阻塞,直到 mysql 进程读取数据。
09.RENAME TABLE语句
- 使用
RENAME TABLE。参见第 15.1.36 节,“RENAME TABLE 语句”。
语法:
RENAME TABLE tbl_name TO new_tbl_name [, tbl_name2 TO new_tbl_name2] ...
RENAME TABLE 重命名一个或多个表。您必须对原表具有 ALTER 和 DROP 权限,并对新表具有 CREATE 和 INSERT 权限。
例如,要将名为 old_table 的表重命名为 new_table,请使用以下语句:
RENAME TABLE old_table TO new_table;
该语句等效于以下 ALTER TABLE 语句:
ALTER TABLE old_table RENAME new_table;
与 ALTER TABLE 不同,RENAME TABLE 可以在单个语句中重命名多个表:
RENAME TABLE old_table1 TO new_table1, old_table2 TO new_table2, old_table3 TO new_table3;
重命名操作从左到右执行。因此,要交换两个表名,可以这样做(假设名为 tmp_table 的中间表尚不存在):
RENAME TABLE old_table TO tmp_table, new_table TO old_table, tmp_table TO new_table;
表的元数据锁按名称顺序获取,在某些情况下,当多个事务并发执行时,这可能会影响操作结果。参见第 10.11.4 节“元数据锁定”。
从 MySQL 8.0.13 开始,您可以重命名被 LOCK TABLES 语句锁定的表,前提是这些表被 WRITE 锁锁定,或者是多表重命名操作中早期步骤重命名 WRITE 锁定的表所产生的表。例如,以下操作是允许的:
LOCK TABLE old_table1 WRITE; RENAME TABLE old_table1 TO new_table1, new_table1 TO new_table2;
以下操作是不允许的:
LOCK TABLE old_table1 READ; RENAME TABLE old_table1 TO new_table1, new_table1 TO new_table2;
在 MySQL 8.0.13 之前,执行 RENAME TABLE 时,不能有任何表被 LOCK TABLES 锁定。
满足事务表锁定条件后,重命名操作以原子方式完成;在重命名进行期间,其他会话无法访问任何相关表。
如果在 RENAME TABLE 期间发生任何错误,该语句将失败且不会进行任何更改。
您可以使用 RENAME TABLE 将表从一个数据库移动到另一个数据库:
RENAME TABLE current_db.tbl_name TO other_db.tbl_name;
使用此方法将所有表从一个数据库移动到另一个数据库,实际上相当于重命名了数据库(这是 MySQL 没有单一语句支持的操作),只是原数据库仍然存在,尽管其中没有表。
与 RENAME TABLE 类似,ALTER TABLE … RENAME 也可用于将表移动到不同的数据库。无论使用哪种语句,如果重命名操作会将表移动到位于不同文件系统的数据库,则操作的成功与否取决于平台,并取决于用于移动表文件的底层操作系统调用。
如果表有触发器,尝试将表重命名到不同的数据库将失败,并返回“触发器位于错误的模式中”(ER_TRG_IN_WRONG_SCHEMA)错误。
未加密的表可以移动到启用加密的数据库,反之亦然。但是,如果启用了 table_encryption_privilege_check 变量,且表加密设置与默认数据库加密不同,则需要 TABLE_ENCRYPTION_ADMIN 权限。
要重命名 TEMPORARY 表,RENAME TABLE 无效。请改用 ALTER TABLE。
RENAME TABLE 适用于视图,但不能将视图重命名到不同的数据库。
专门为重命名的表或视图授予的任何权限不会迁移到新名称。必须手动更改这些权限。
RENAME TABLE tbl_name TO new_tbl_name
会更改内部生成的外键约束名称以及以字符串“tbl_name_ibfk_”开头的用户定义外键约束名称,以反映新表名。InnoDB
将以字符串“tbl_name_ibfk_”开头的外键约束名称解释为内部生成的名称。
指向重命名表的外键约束名称会自动更新,除非发生冲突,此时语句将失败并报错。如果重命名的约束名称已存在,则会发生冲突。在这种情况下,您必须删除并重新创建外键,以便它们正常运行。
RENAME TABLE tbl_name TO new_tbl_name
会更改内部生成的以及以字符串“tbl_name_ch k_”开头的用户定义 CHECK 约束名称,以反映新表名。MySQL
将以字符串“tbl_name_ch k_”开头的 CHECK 约束名称解释为内部生成的名称。示例:
mysql> SHOW CREATE TABLE t1\G *************************** 1. row *************************** Table: t1 Create Table: CREATE TABLE `t1` ( `i1` int(11) DEFAULT NULL, `i2` int(11) DEFAULT NULL, CONSTRAINT `t1_ch k_1` CHECK ((`i1` > 0)), CONSTRAINT `t1_ch k_2` CHECK ((`i2` < 0)) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci 1 row in set (0.02 sec) mysql> RENAME TABLE t1 TO t3; Query OK, 0 rows affected (0.03 sec) mysql> SHOW CREATE TABLE t3\G *************************** 1. row *************************** Table: t3 Create Table: CREATE TABLE `t3` ( `i1` int(11) DEFAULT NULL, `i2` int(11) DEFAULT NULL, CONSTRAINT `t3_ch k_1` CHECK ((`i1` > 0)), CONSTRAINT `t3_ch k_2` CHECK ((`i2` < 0)) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci 1 row in set (0.01 sec)
10.REPLACE语句
- 使用
REPLACE代替DELETE加INSERT。参见第 15.2.12 节,“REPLACE 语句”。
语法:
REPLACE [LOW_PRIORITY | DELAYED]
[INTO] tbl_name
[PARTITION (partition_name [, partition_name] ...)]
[(col_name [, col_name] ...)]
{ {VALUES | VALUE} (value_list) [, (value_list)] ...
|
VALUES row_constructor_list
}
REPLACE [LOW_PRIORITY | DELAYED]
[INTO] tbl_name
[PARTITION (partition_name [, partition_name] ...)]
SET assignment_list
REPLACE [LOW_PRIORITY | DELAYED]
[INTO] tbl_name
[PARTITION (partition_name [, partition_name] ...)]
[(col_name [, col_name] ...)]
{SELECT ... | TABLE table_name}
value:
{expr | DEFAULT}
value_list:
value [, value] ...
row_constructor_list:
ROW(value_list)[, ROW(value_list)][, ...]
assignment:
col_name = value
assignment_list:
assignment [, assignment] ...
REPLACE 的工作方式与 INSERT 完全相同,只是如果表中的旧行与新行在主键或唯一索引上具有相同的值,则在插入新行之前会删除旧行。参见第 15.2.7 节“INSERT 语句”。
REPLACE 是 MySQL 对 SQL 标准的扩展。它要么插入,要么删除并插入。对于 MySQL 对标准 SQL 的另一个扩展——即插入或更新——请参见第 15.2.7.2 节“INSERT … ON DUPLICATE KEY UPDATE 语句”。
在 MySQL 5.6 中,DELAYED 插入和替换已被弃用。在 MySQL 8.0 中,不支持 DELAYED。服务器会识别但忽略
DELAYED 关键字,将替换作为非延迟替换处理,并生成一个 ER_WARN_LEGACY_SYNTAX_CONVERTED
警告:REPLACE DELAYED 不再受支持。该语句已转换为 REPLACE。DELAYED 关键字计划在未来的版本中移除。
注意
REPLACE 仅当表具有主键或唯一索引时才有意义。否则,它等同于 INSERT,因为没有索引可用于确定新行是否重复其他行。
所有列的值都取自 REPLACE 语句中指定的值。任何缺失的列都设置为其默认值,就像 INSERT 一样。您不能引用当前行中的值并在新行中使用它们。如果您使用诸如
SET col_name = col_name + 1 这样的赋值,右侧对列名的引用将被视为
DEFAULT(col_name),因此该赋值等同于
SET col_name = DEFAULT(col_name) + 1。
在 MySQL 8.0.19 及更高版本中,您可以使用 VALUES ROW() 指定 REPLACE 尝试插入的列值。
要使用 REPLACE,您必须对表同时具有 INSERT 和 DELETE 权限。
如果显式替换生成列,则唯一允许的值是 DEFAULT。有关生成列的信息,请参见第 15.1.20.8 节“CREATE TABLE 和生成列”。
REPLACE 支持使用 PARTITION 子句进行显式分区选择,该子句包含一个逗号分隔的分区、子分区或两者的名称列表。与 INSERT 一样,如果无法将新行插入到这些分区或子分区中的任何一个,REPLACE 语句将失败并显示错误
Found a row not matching the given partition set。有关更多信息和示例,请参见第 26.5 节“分区选择”。
REPLACE 语句返回一个计数以指示受影响的行数。这是删除和插入的行数之和。如果单行 REPLACE 的计数为
1,则表示插入了行且没有删除任何行。如果计数大于
1,则表示在插入新行之前删除了一个或多个旧行。如果表包含多个唯一索引,并且新行在不同唯一索引中与不同旧行的值重复,则单行替换多个旧行是可能的。
受影响行数使得很容易确定 REPLACE 是仅添加了一行还是也替换了某些行:检查计数是 1(添加)还是更大(替换)。
如果您使用 C API,可以通过 mysql_affected_rows() 函数获取受影响行数。
您不能替换到表中并在子查询中从同一表中选择。
MySQL 对 REPLACE(以及 LOAD DATA … REPLACE)使用以下算法:
- 尝试将新行插入到表中。
- 当因主键或唯一索引发生重复键错误而导致插入失败时:
a. 从表中删除具有重复键值的冲突行。
b. 再次尝试将新行插入表中。
在重复键错误的情况下,存储引擎可能将 REPLACE 作为更新而不是删除加插入来执行,但语义是相同的。除了存储引擎递增 Handler_xxx 状态变量的方式可能不同外,没有其他用户可见的影响。
由于 REPLACE … SELECT 语句的结果取决于 SELECT 的行顺序,而此顺序并不总是可以保证,因此在记录这些语句时,源和副本可能会出现差异。因此,REPLACE … SELECT 语句被标记为基于语句的复制不安全。在使用基于语句的模式时,此类语句会在错误日志中产生警告,而在使用 MIXED 模式时,会使用基于行的格式写入二进制日志。另请参见第 19.2.1.1 节“基于语句和基于行的复制的优缺点”。
MySQL 8.0.19 及更高版本支持将 TABLE 以及 SELECT 与 REPLACE 一起使用,就像与 INSERT 一起使用一样。更多信息和示例,请参见第 15.2.7.1 节“INSERT … SELECT 语句”。
在修改未分区的现有表以适应分区时,或者在修改已分区表的分区时,您可能需要更改表的主键(参见第 26.6.1 节“分区键、主键和唯一键”)。您应该注意,如果这样做,REPLACE 语句的结果可能会受到影响,就像您修改了非分区表的主键一样。考虑由以下 CREATE TABLE 语句创建的表:
CREATE TABLE test ( id INT UNSIGNED NOT NULL AUTO_INCREMENT, data VARCHAR(64) DEFAULT NULL, ts TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id) );
当我们创建此表并在 mysql 客户端中运行所示语句时,结果如下:
mysql> REPLACE INTO test VALUES (1, 'Old', '2014-08-20 18:47:00'); Query OK, 1 row affected (0.04 sec) mysql> REPLACE INTO test VALUES (1, 'New', '2014-08-20 18:47:42'); Query OK, 2 rows affected (0.04 sec) mysql> SELECT * FROM test; +----+------+---------------------+ | id | data | ts | +----+------+---------------------+ | 1 | New | 2014-08-20 18:47:42 | +----+------+---------------------+ 1 row in set (0.00 sec)
现在我们创建第二个表,几乎与第一个表相同,只是现在主键涵盖 2 列,如下所示(强调文本):
CREATE TABLE test2 ( id INT UNSIGNED NOT NULL AUTO_INCREMENT, data VARCHAR(64) DEFAULT NULL, ts TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id, ts) );
当我们在 test2 上运行与原始 test 表相同的两个 REPLACE 语句时,我们得到不同的结果:
mysql> REPLACE INTO test2 VALUES (1, 'Old', '2014-08-20 18:47:00'); Query OK, 1 row affected (0.05 sec) mysql> REPLACE INTO test2 VALUES (1, 'New', '2014-08-20 18:47:42'); Query OK, 1 row affected (0.06 sec) mysql> SELECT * FROM test2; +----+------+---------------------+ | id | data | ts | +----+------+---------------------+ | 1 | Old | 2014-08-20 18:47:00 | | 1 | New | 2014-08-20 18:47:42 | +----+------+---------------------+ 2 rows in set (0.00 sec)
这是因为在 test2 上运行时,id 和 ts 列值都必须与现有行的值匹配才能替换该行;否则,将插入一行。
11.ALTER TABLE CHANGE col_name等
- 在
ALTER TABLE语句中使用CHANGE col_name、DROP col_name或DROP INDEX、IGNORE或RENAME。在ALTER TABLE语句中使用多个ADD、ALTER、DROP或CHANGE子句。参见第 15.1.9 节,“ALTER TABLE 语句”。
语法:
ALTER TABLE tbl_name
[alter_option [, alter_option] ...]
[partition_options]
alter_option: {
table_options
| ADD [COLUMN] col_name column_definition
[FIRST | AFTER col_name]
| ADD [COLUMN] (col_name column_definition,...)
| ADD {INDEX | KEY} [index_name]
[index_type] (key_part,...) [index_option] ...
| ADD {FULLTEXT | SPATIAL} [INDEX | KEY] [index_name]
(key_part,...) [index_option] ...
| ADD [CONSTRAINT [symbol]] PRIMARY KEY
[index_type] (key_part,...)
[index_option] ...
| ADD [CONSTRAINT [symbol]] UNIQUE [INDEX | KEY]
[index_name] [index_type] (key_part,...)
[index_option] ...
| ADD [CONSTRAINT [symbol]] FOREIGN KEY
[index_name] (col_name,...)
reference_definition
| ADD [CONSTRAINT [symbol]] CHECK (expr) [[NOT] ENFORCED]
| DROP {CHECK | CONSTRAINT} symbol
| ALTER {CHECK | CONSTRAINT} symbol [NOT] ENFORCED
| ALGORITHM [=] {DEFAULT | INSTANT | INPLACE | COPY}
| ALTER [COLUMN] col_name {
SET DEFAULT {literal | (expr)}
| SET {VISIBLE | INVISIBLE}
| DROP DEFAULT
}
| ALTER INDEX index_name {VISIBLE | INVISIBLE}
| CHANGE [COLUMN] old_col_name new_col_name column_definition
[FIRST | AFTER col_name]
| [DEFAULT] CHARACTER SET [=] charset_name [COLLATE [=] collation_name]
| CONVERT TO CHARACTER SET charset_name [COLLATE collation_name]
| {DISABLE | ENABLE} KEYS
| {DISCARD | IMPORT} TABLESPACE
| DROP [COLUMN] col_name
| DROP {INDEX | KEY} index_name
| DROP PRIMARY KEY
| DROP FOREIGN KEY fk_symbol
| FORCE
| LOCK [=] {DEFAULT | NONE | SHARED | EXCLUSIVE}
| MODIFY [COLUMN] col_name column_definition
[FIRST | AFTER col_name]
| ORDER BY col_name [, col_name] ...
| RENAME COLUMN old_col_name TO new_col_name
| RENAME {INDEX | KEY} old_index_name TO new_index_name
| RENAME [TO | AS] new_tbl_name
| {WITHOUT | WITH} VALIDATION
}
partition_options:
partition_option [partition_option] ...
partition_option: {
ADD PARTITION (partition_definition)
| DROP PARTITION partition_names
| DISCARD PARTITION {partition_names | ALL} TABLESPACE
| IMPORT PARTITION {partition_names | ALL} TABLESPACE
| TRUNCATE PARTITION {partition_names | ALL}
| COALESCE PARTITION number
| REORGANIZE PARTITION partition_names INTO (partition_definitions)
| EXCHANGE PARTITION partition_name WITH TABLE tbl_name [{WITH | WITHOUT} VALIDATION]
| ANALYZE PARTITION {partition_names | ALL}
| CHECK PARTITION {partition_names | ALL}
| OPTIMIZE PARTITION {partition_names | ALL}
| REBUILD PARTITION {partition_names | ALL}
| REPAIR PARTITION {partition_names | ALL}
| REMOVE PARTITIONING
}
key_part: {col_name [(length)] | (expr)} [ASC | DESC]
index_type:
USING {BTREE | HASH}
index_option: {
KEY_BLOCK_SIZE [=] value
| index_type
| WITH PARSER parser_name
| COMMENT 'string'
| {VISIBLE | INVISIBLE}
}
table_options:
table_option [[,] table_option] ...
table_option: {
AUTOEXTEND_SIZE [=] value
| AUTO_INCREMENT [=] value
| AVG_ROW_LENGTH [=] value
| [DEFAULT] CHARACTER SET [=] charset_name
| CHECKSUM [=] {0 | 1}
| [DEFAULT] COLLATE [=] collation_name
| COMMENT [=] 'string'
| COMPRESSION [=] {'ZLIB' | 'LZ4' | 'NONE'}
| CONNECTION [=] 'connect_string'
| {DATA | INDEX} DIRECTORY [=] 'absolute path to directory'
| DELAY_KEY_WRITE [=] {0 | 1}
| ENCRYPTION [=] {'Y' | 'N'}
| ENGINE [=] engine_name
| ENGINE_ATTRIBUTE [=] 'string'
| INSERT_METHOD [=] { NO | FIRST | LAST }
| KEY_BLOCK_SIZE [=] value
| MAX_ROWS [=] value
| MIN_ROWS [=] value
| PACK_KEYS [=] {0 | 1 | DEFAULT}
| PASSWORD [=] 'string'
| ROW_FORMAT [=] {DEFAULT | DYNAMIC | FIXED | COMPRESSED | REDUNDANT | COMPACT}
| SECONDARY_ENGINE_ATTRIBUTE [=] 'string'
| STATS_AUTO_RECALC [=] {DEFAULT | 0 | 1}
| STATS_PERSISTENT [=] {DEFAULT | 0 | 1}
| STATS_SAMPLE_PAGES [=] value
| TABLESPACE tablespace_name [STORAGE {DISK | MEMORY}]
| UNION [=] (tbl_name[,tbl_name]...)
}
partition_options:
(see CREATE TABLE options)
CHANGE 是 MySQL 对标准 SQL 的扩展。MODIFY 和 RENAME COLUMN 是 MySQL 为兼容 Oracle 而引入的扩展。
要更改列的名称和定义,请使用 CHANGE,指定旧名称、新名称以及新定义。例如,要将一个 INT NOT NULL 列从 a 重命名为 b,并将其定义更改为使用 BIGINT 数据类型,同时保留 NOT NULL 属性,请执行以下操作:
ALTER TABLE t1 CHANGE a b BIGINT NOT NULL;
要更改列定义但不更改其名称,请使用 CHANGE 或 MODIFY。使用 CHANGE 时,语法要求两个列名,因此您必须指定相同的名称两次以保持名称不变。例如,要更改列 b 的定义,请执行以下操作:
ALTER TABLE t1 CHANGE b b INT NOT NULL;
MODIFY 更适合在不更改名称的情况下更改定义,因为它只需要列名一次:
ALTER TABLE t1 MODIFY b INT NOT NULL;
要更改列名但不更改其定义,请使用 CHANGE 或 RENAME COLUMN。使用 CHANGE 时,语法要求指定列定义,因此要保留原有定义,您必须重新指定该列当前的定义。例如,要将一个 INT NOT NULL 列从 b 重命名为 a,请执行以下操作:
ALTER TABLE t1 CHANGE b a INT NOT NULL;
RENAME COLUMN 更适合在不更改定义的情况下更改名称,因为它只需要旧名称和新名称:
ALTER TABLE t1 RENAME COLUMN b TO a;
通常,您不能将列重命名为表中已存在的名称。但有时也存在例外情况,例如交换名称或循环移动时。如果表中有名为 a、b 和 c 的列,则以下操作是有效的:
– 交换 a 和 b
ALTER TABLE t1 RENAME COLUMN a TO b, RENAME COLUMN b TO a;
– 在 a、b、c 之间"轮转"
ALTER TABLE t1 RENAME COLUMN a TO b, RENAME COLUMN b TO c, RENAME COLUMN c TO a;
12.CREATE TABLE语句中使用索引
- 在
CREATE TABLE语句中使用索引名、列前缀索引以及INDEX或KEY。参见第 15.1.20 节,“CREATE TABLE 语句”。
语法:
CREATE [TEMPORARY] TABLE [IF NOT EXISTS] tbl_name
(create_definition,...)
[table_options]
[partition_options]
CREATE [TEMPORARY] TABLE [IF NOT EXISTS] tbl_name
[(create_definition,...)]
[table_options]
[partition_options]
[IGNORE | REPLACE]
[AS] query_expression
CREATE [TEMPORARY] TABLE [IF NOT EXISTS] tbl_name
{ LIKE old_tbl_name | (LIKE old_tbl_name) }
create_definition: {
col_name column_definition
| {INDEX | KEY} [index_name] [index_type] (key_part,...)
[index_option] ...
| {FULLTEXT | SPATIAL} [INDEX | KEY] [index_name] (key_part,...)
[index_option] ...
| [CONSTRAINT [symbol]] PRIMARY KEY
[index_type] (key_part,...)
[index_option] ...
| [CONSTRAINT [symbol]] UNIQUE [INDEX | KEY]
[index_name] [index_type] (key_part,...)
[index_option] ...
| [CONSTRAINT [symbol]] FOREIGN KEY
[index_name] (col_name,...)
reference_definition
| check_constraint_definition
}
column_definition: {
data_type [NOT NULL | NULL] [DEFAULT {literal | (expr)} ]
[VISIBLE | INVISIBLE]
[AUTO_INCREMENT] [UNIQUE [KEY]] [[PRIMARY] KEY]
[COMMENT 'string']
[COLLATE collation_name]
[COLUMN_FORMAT {FIXED | DYNAMIC | DEFAULT}]
[ENGINE_ATTRIBUTE [=] 'string']
[SECONDARY_ENGINE_ATTRIBUTE [=] 'string']
[STORAGE {DISK | MEMORY}]
[reference_definition]
[check_constraint_definition]
| data_type
[COLLATE collation_name]
[GENERATED ALWAYS] AS (expr)
[VIRTUAL | STORED] [NOT NULL | NULL]
[VISIBLE | INVISIBLE]
[UNIQUE [KEY]] [[PRIMARY] KEY]
[COMMENT 'string']
[reference_definition]
[check_constraint_definition]
}
data_type:
(see Chapter 13, Data Types)
key_part: {col_name [(length)] | (expr)} [ASC | DESC]
index_type:
USING {BTREE | HASH}
index_option: {
KEY_BLOCK_SIZE [=] value
| index_type
| WITH PARSER parser_name
| COMMENT 'string'
| {VISIBLE | INVISIBLE}
|ENGINE_ATTRIBUTE [=] 'string'
|SECONDARY_ENGINE_ATTRIBUTE [=] 'string'
}
check_constraint_definition:
[CONSTRAINT [symbol]] CHECK (expr) [[NOT] ENFORCED]
reference_definition:
REFERENCES tbl_name (key_part,...)
[MATCH FULL | MATCH PARTIAL | MATCH SIMPLE]
[ON DELETE reference_option]
[ON UPDATE reference_option]
reference_option:
RESTRICT | CASCADE | SET NULL | NO ACTION | SET DEFAULT
table_options:
table_option [[,] table_option] ...
table_option: {
AUTOEXTEND_SIZE [=] value
| AUTO_INCREMENT [=] value
| AVG_ROW_LENGTH [=] value
| [DEFAULT] CHARACTER SET [=] charset_name
| CHECKSUM [=] {0 | 1}
| [DEFAULT] COLLATE [=] collation_name
| COMMENT [=] 'string'
| COMPRESSION [=] {'ZLIB' | 'LZ4' | 'NONE'}
| CONNECTION [=] 'connect_string'
| {DATA | INDEX} DIRECTORY [=] 'absolute path to directory'
| DELAY_KEY_WRITE [=] {0 | 1}
| ENCRYPTION [=] {'Y' | 'N'}
| ENGINE [=] engine_name
| ENGINE_ATTRIBUTE [=] 'string'
| INSERT_METHOD [=] { NO | FIRST | LAST }
| KEY_BLOCK_SIZE [=] value
| MAX_ROWS [=] value
| MIN_ROWS [=] value
| PACK_KEYS [=] {0 | 1 | DEFAULT}
| PASSWORD [=] 'string'
| ROW_FORMAT [=] {DEFAULT | DYNAMIC | FIXED | COMPRESSED | REDUNDANT | COMPACT}
| START TRANSACTION
| SECONDARY_ENGINE_ATTRIBUTE [=] 'string'
| STATS_AUTO_RECALC [=] {DEFAULT | 0 | 1}
| STATS_PERSISTENT [=] {DEFAULT | 0 | 1}
| STATS_SAMPLE_PAGES [=] value
| tablespace_option
| UNION [=] (tbl_name[,tbl_name]...)
}
partition_options:
PARTITION BY
{ [LINEAR] HASH(expr)
| [LINEAR] KEY [ALGORITHM={1 | 2}] (column_list)
| RANGE{(expr) | COLUMNS(column_list)}
| LIST{(expr) | COLUMNS(column_list)} }
[PARTITIONS num]
[SUBPARTITION BY
{ [LINEAR] HASH(expr)
| [LINEAR] KEY [ALGORITHM={1 | 2}] (column_list) }
[SUBPARTITIONS num]
]
[(partition_definition [, partition_definition] ...)]
partition_definition:
PARTITION partition_name
[VALUES
{LESS THAN {(expr | value_list) | MAXVALUE}
|
IN (value_list)}]
[[STORAGE] ENGINE [=] engine_name]
[COMMENT [=] 'string' ]
[DATA DIRECTORY [=] 'data_dir']
[INDEX DIRECTORY [=] 'index_dir']
[MAX_ROWS [=] max_number_of_rows]
[MIN_ROWS [=] min_number_of_rows]
[TABLESPACE [=] tablespace_name]
[(subpartition_definition [, subpartition_definition] ...)]
subpartition_definition:
SUBPARTITION logical_name
[[STORAGE] ENGINE [=] engine_name]
[COMMENT [=] 'string' ]
[DATA DIRECTORY [=] 'data_dir']
[INDEX DIRECTORY [=] 'index_dir']
[MAX_ROWS [=] max_number_of_rows]
[MIN_ROWS [=] min_number_of_rows]
[TABLESPACE [=] tablespace_name]
tablespace_option:
TABLESPACE tablespace_name [STORAGE DISK]
| [TABLESPACE tablespace_name] STORAGE MEMORY
query_expression:
SELECT ... (Some valid select or union statement)
示例如下:
CREATE TABLE lookup (id INT, INDEX USING BTREE (id)) ENGINE = MEMORY;
13.IF NOT EXISTS、IF EXISTS语句
- 在
CREATE TABLE中使用TEMPORARY或IF NOT EXISTS。 - 在
DROP TABLE和DROP DATABASE中使用IF EXISTS。
语法:
CREATE [TEMPORARY] TABLE [IF NOT EXISTS] tbl_name
(create_definition,...)
[table_options]
[partition_options]
CREATE [TEMPORARY] TABLE [IF NOT EXISTS] tbl_name
[(create_definition,...)]
[table_options]
[partition_options]
[IGNORE | REPLACE]
[AS] query_expression
CREATE [TEMPORARY] TABLE [IF NOT EXISTS] tbl_name
{ LIKE old_tbl_name | (LIKE old_tbl_name) }
DROP [TEMPORARY] TABLE [IF EXISTS] tbl_name [, tbl_name] ... [RESTRICT | CASCADE]
DROP {DATABASE | SCHEMA} [IF EXISTS] db_name
mysql> create database if not exists cjc1; Query OK, 1 row affected (0.32 sec) mysql> create table if not exists cjc.t1(id int); Query OK, 0 rows affected, 1 warning (0.31 sec) mysql> show warnings; +-------+------+---------------------------+ | Level | Code | Message | +-------+------+---------------------------+ | Note | 1050 | Table 't1' already exists | +-------+------+---------------------------+ 1 row in set (0.00 sec) mysql> drop database if exists cjc1; Query OK, 0 rows affected (0.37 sec)
14.DROP TABLE 多表
- 能够使用单个
DROP TABLE语句删除多个表。
语法:
DROP [TEMPORARY] TABLE [IF EXISTS] tbl_name [, tbl_name] ... [RESTRICT | CASCADE]
示例(MySQL 8.0.36):
mysql> create table t3 as select * from t2; Query OK, 5 rows affected (0.54 sec) Records: 5 Duplicates: 0 Warnings: 0 mysql> drop table t2,t3,t4; ERROR 1051 (42S02): Unknown table 'cjc.t4' mysql> show tables; +---------------+ | Tables_in_cjc | +---------------+ | t1 | | t2 | | t3 | +---------------+ 3 rows in set (0.00 sec) mysql> drop table t2,t3; Query OK, 0 rows affected (0.53 sec)
15.UPDATE和DELETE语句ORDER BY、LIMIT子句
-
UPDATE和DELETE语句中的ORDER BY和LIMIT子句。
语法:
UPDATE [LOW_PRIORITY] [IGNORE] table_reference SET assignment_list [WHERE where_condition] [ORDER BY ...] [LIMIT row_count]
示例如下:
例如,如果表中 id 列包含值 1 和 2,且先将 1 更新为 2,再将 2 更新为 3,则会发生错误。为避免此问题,可添加 ORDER BY 子句使 id 值较大的行先于较小值的行进行更新:
UPDATE t SET id = id + 1 ORDER BY id DESC;
您可以使用 LIMIT row_count 来限制 UPDATE 的范围。LIMIT 子句是一种行匹配限制。一旦找到满足 WHERE 子句的 row_count 行,该语句就会停止,无论这些行是否实际被更改。
mysql> update cjc.t1 set id=id+1; Query OK, 3 rows affected (0.30 sec) Rows matched: 3 Changed: 3 Warnings: 0 mysql> select * from cjc.t1; +------+------+----------+ | id | name | time | +------+------+----------+ | 3 | x | 13:33:21 | | 4 | y | 13:33:21 | | 5 | z | 13:56:47 | +------+------+----------+ 3 rows in set (0.00 sec) mysql> update cjc.t1 set id=id+1 order by id desc; Query OK, 3 rows affected (0.31 sec) Rows matched: 3 Changed: 3 Warnings: 0 mysql> select * from cjc.t1; +------+------+----------+ | id | name | time | +------+------+----------+ | 4 | x | 13:33:21 | | 5 | y | 13:33:21 | | 6 | z | 13:56:47 | +------+------+----------+ 3 rows in set (0.00 sec) mysql> update cjc.t1 set id=id+1 limit 1; Query OK, 1 row affected (0.30 sec) Rows matched: 1 Changed: 1 Warnings: 0 mysql> select * from cjc.t1; +------+------+----------+ | id | name | time | +------+------+----------+ | 5 | x | 13:33:21 | | 5 | y | 13:33:21 | | 6 | z | 13:56:47 | +------+------+----------+ 3 rows in set (0.01 sec)
16.INSERT INTO SET 语句
-
INSERT INTO tbl_name SET col_name = ...语法。
语法:
INSERT [LOW_PRIORITY | DELAYED | HIGH_PRIORITY] [IGNORE] [INTO] tbl_name [PARTITION (partition_name [, partition_name] ...)] SET assignment_list [AS row_alias[(col_alias [, col_alias] ...)]] [ON DUPLICATE KEY UPDATE assignment_list]
示例:
mysql> insert into cjc.t1 set name='a',time=now(); Query OK, 1 row affected (0.02 sec) mysql> select * from cjc.t1; +------+------+----------+ | id | name | time | +------+------+----------+ | 5 | x | 13:33:21 | | 5 | y | 13:33:21 | | 6 | z | 13:56:47 | | NULL | a | 13:59:08 | +------+------+----------+ 4 rows in set (0.00 sec)
17.INSERT和REPLACE语句中的DELAYED子句
-
INSERT和REPLACE语句中的DELAYED子句。
语法:
INSERT DELAYED ...
INSERT 语句的 DELAYED 选项是 MySQL 对标准 SQL 的扩展。在 MySQL 的早期版本中,它可用于某些类型的表(例如
MyISAM),当客户端使用 INSERT DELAYED
时,它会立即从服务器获得确认,并且该行会被排队,当表没有被任何其他线程使用时再执行插入。
DELAYED 插入和替换在 MySQL 5.6 中已被弃用。在 MySQL 8.0 中,不支持 DELAYED。服务器会识别但忽略
DELAYED 关键字,将插入作为非延迟插入处理,并生成一个 ER_WARN_LEGACY_SYNTAX_CONVERTED 警告:INSERT
DELAYED 不再受支持。该语句已转换为 INSERT。DELAYED 关键字计划在未来的版本中移除。
18.LOW_PRIORITY子句
-
INSERT、REPLACE、DELETE和UPDATE语句中的LOW_PRIORITY子句。
INSERT 语句支持以下修饰符: - 若使用 LOW_PRIORITY 修饰符,INSERT 的执行会被延迟,直到没有其他客户端从该表读取数据为止。这包括在现有客户端正在读取时开始读取的其他客户端,以及在 INSERT LOW_PRIORITY 语句等待期间开始读取的客户端。因此,发出 INSERT LOW_PRIORITY 语句的客户端可能会等待很长时间。
- LOW_PRIORITY 仅影响使用表级锁定的存储引擎(例如 MyISAM、MEMORY 和 MERGE)。
注意
通常不应将 LOW_PRIORITY 用于 MyISAM 表,因为这样做会禁用并发插入。参见第 10.11.3 节“并发插入”。
19.SELECT语句 INTO OUTFILE、INTO DUMPFILE
- 在
SELECT语句中使用INTO OUTFILE或INTO DUMPFILE。参见第 15.2.13 节,“SELECT 语句”。
语法:
SELECT
[ALL | DISTINCT | DISTINCTROW ]
[HIGH_PRIORITY]
[STRAIGHT_JOIN]
[SQL_SMALL_RESULT] [SQL_BIG_RESULT] [SQL_BUFFER_RESULT]
[SQL_NO_CACHE] [SQL_CALC_FOUND_ROWS]
select_expr [, select_expr] ...
[into_option]
[FROM table_references
[PARTITION partition_list]]
[WHERE where_condition]
[GROUP BY {col_name | expr | position}, ... [WITH ROLLUP]]
[HAVING where_condition]
[WINDOW window_name AS (window_spec)
[, window_name AS (window_spec)] ...]
[ORDER BY {col_name | expr | position}
[ASC | DESC], ... [WITH ROLLUP]]
[LIMIT {[offset,] row_count | row_count OFFSET offset}]
[into_option]
[FOR {UPDATE | SHARE}
[OF tbl_name [, tbl_name] ...]
[NOWAIT | SKIP LOCKED]
| LOCK IN SHARE MODE]
[into_option]
into_option: {
INTO OUTFILE 'file_name'
[CHARACTER SET charset_name]
export_options
| INTO DUMPFILE 'file_name'
| INTO var_name [, var_name] ...
}
export_options:
[{FIELDS | COLUMNS}
[TERMINATED BY 'string']
[[OPTIONALLY] ENCLOSED BY 'char']
[ESCAPED BY 'char']
]
[LINES
[STARTING BY 'string']
[TERMINATED BY 'string']
]
SELECT … INTO 形式的 SELECT 语句可将查询结果存储到变量或写入文件:
- SELECT … INTO var_list 用于选取列值并存储到变量中。
- SELECT … INTO OUTFILE 将选中的行写入文件。可以指定列和行的终止符以生成特定的输出格式。
- SELECT … INTO DUMPFILE 将单行写入文件,不进行任何格式化。
一个给定的 SELECT 语句最多只能包含一个 INTO 子句。然而,如 SELECT 语法描述所示(参见第 15.2.13 节“SELECT 语句”),INTO 可以出现在不同位置:
- 在 FROM 子句之前。示例:
SELECT * INTO @myvar FROM t1; - 在尾随的锁定子句之前。示例:
SELECT * FROM t1 INTO @myvar FOR UPDATE; - 在 SELECT 语句的末尾。示例:
SELECT * FROM t1 FOR UPDATE INTO @myvar;
语句末尾的 INTO 位置从 MySQL 8.0.20 开始支持,并且是推荐的位置。从 MySQL 8.0.20 起,锁定子句之前的位置已被弃用;预计在未来 MySQL 版本中将移除对其的支持。换句话说,INTO 出现在 FROM 之后但不在 SELECT 末尾时会产生警告。
不应在嵌套的 SELECT 中使用 INTO 子句,因为这样的 SELECT 必须将其结果返回给外部上下文。在 UNION 语句中使用 INTO 也有约束;参见第 15.2.18 节“UNION 子句”。
对于
INTO var_list 这种形式:
- var_list 指定一个或多个变量的列表,每个变量可以是用户定义变量、存储过程或函数参数,或存储程序的局部变量。(在预处理语句
SELECT ... INTO var_list中,只允许使用用户定义变量;参见第 15.6.4.2 节“局部变量的作用域和解析”。) - 选中的值被赋值给变量。变量数量必须与列数匹配。查询应返回单行。如果查询未返回行,则会产生错误代码为 1329 的警告(No
data),并且变量值保持不变。如果查询返回多行,则会发生错误 1172(Result consisted of more than one
row)。如果语句可能检索到多行,可以使用
LIMIT 1将结果集限制为单行。
SELECT id, data INTO @x, @y FROM test.t1 LIMIT 1;
INTO var_list 也可以与 TABLE 语句一起使用,但需遵守以下限制:
- 变量数量必须与表中的列数匹配。
- 如果表包含多行,必须使用
LIMIT 1将结果集限制为单行。LIMIT 1必须位于 INTO 关键字之前。
此类语句的示例如下:
TABLE employees ORDER BY lname DESC LIMIT 1 INTO @id, @fname, @lname, @hired, @separated, @job_code, @store_id;
您也可以从生成单行的 VALUES 语句中选择值,并将其存储到一组用户变量中。在这种情况下,必须使用表别名,并且必须将值列表中的每个值分配给一个变量。以下两个语句均等同于
SET @x=2, @y=4, @z=8:
SELECT * FROM (VALUES ROW(2,4,8)) AS t INTO @x,@y,@z; SELECT * FROM (VALUES ROW(2,4,8)) AS t(a,b,c) INTO @x,@y,@z;
用户变量名不区分大小写。参见第 11.4 节“用户定义变量”。
SELECT ... INTO OUTFILE 'file_name' 形式的 SELECT 将选中的行写入文件。该文件在服务器主机上创建,因此您必须拥有 FILE 权限才能使用此语法。file_name 不能是已存在的文件,这尤其防止了修改诸如
/etc/passwd 和数据库表等文件。character_set_filesystem 系统变量控制文件名的解释。
SELECT … INTO OUTFILE 语句旨在将表转储到服务器主机上的文本文件。要在其他主机上创建结果文件,SELECT … INTO
OUTFILE
通常不合适,因为无法写入相对于服务器主机文件系统的文件路径,除非可以通过服务器主机文件系统上的网络映射路径访问远程主机上的文件位置。
或者,如果在远程主机上安装了 MySQL 客户端软件,您可以使用诸如
mysql -e "SELECT ..." > file_name 的客户端命令在该主机上生成文件。
SELECT … INTO OUTFILE 是 LOAD DATA 的互补操作。列值在写入时转换为 CHARACTER SET
子句中指定的字符集。如果没有指定此类子句,则使用二进制字符集转储值。实际上,不进行字符集转换。如果结果集包含多个字符集的列,输出数据文件也是如此,并且可能无法正确重新加载文件。
语句中 export_options 部分的语法由与 LOAD DATA 语句相同的 FIELDS 和 LINES 子句组成。有关 FIELDS 和 LINES 子句的详细信息,包括其默认值和允许值,请参见第 15.2.9 节“LOAD DATA 语句”。
FIELDS ESCAPED BY 控制如何写入特殊字符。如果 FIELDS ESCAPED BY 字符不为空,则在必要时将其用作前缀以避免输出中后续字符的歧义:
- FIELDS ESCAPED BY 字符本身
- FIELDS [OPTIONALLY] ENCLOSED BY 字符
- FIELDS TERMINATED BY 和 LINES TERMINATED BY 值的第一个字符
- ASCII NUL(零值字节;实际写入转义字符后的是 ASCII 0,而不是零值字节)
FIELDS TERMINATED BY、ENCLOSED BY、ESCAPED BY 或 LINES TERMINATED BY 字符必须被转义,以便能够可靠地读回文件。ASCII NUL 被转义以便用某些分页器查看时更容易。
生成的文件不需要符合 SQL 语法,因此其他内容无需转义。
如果 FIELDS ESCAPED BY 字符为空,则不转义任何字符,并且 NULL 输出为 NULL,而不是 \N。指定空的转义字符可能不是一个好主意,特别是当数据中的字段值包含刚才列出的任何字符时。
当您希望将表的所有列转储到文本文件时,INTO OUTFILE 也可以与 TABLE 语句一起使用。在这种情况下,可以使用 ORDER BY 和 LIMIT 控制行的顺序和数量;这些子句必须位于 INTO OUTFILE 之前。
TABLE ... INTO OUTFILE 支持与
SELECT ... INTO OUTFILE 相同的 export_options,并且受相同的文件系统写入限制。此类语句的示例如下:
TABLE employees ORDER BY lname LIMIT 1000 INTO OUTFILE '/tmp/employee_data_1.txt' FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '"', ESCAPED BY '\' LINES TERMINATED BY '\n';
您也可以将
SELECT ... INTO OUTFILE 与 VALUES 语句一起使用,直接将值写入文件。示例如下:
SELECT * FROM (VALUES ROW(1,2,3),ROW(4,5,6),ROW(7,8,9)) AS t INTO OUTFILE '/tmp/select-values.txt';
您必须使用表别名;也支持列别名,并可选择性地用于仅从所需列写入值。您还可以使用 SELECT … INTO OUTFILE 支持的任何或所有导出选项来格式化输出到文件。
以下是一个生成许多程序使用的逗号分隔值 (CSV) 格式文件的示例:
SELECT a,b,a+b INTO OUTFILE '/tmp/result.txt' FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '"' LINES TERMINATED BY '\n' FROM test_table;
如果使用 INTO DUMPFILE 代替 INTO OUTFILE,MySQL 仅将一行写入文件,不进行任何列或行终止,也不执行任何转义处理。这对于选择 BLOB 值并将其存储在文件中很有用。
TABLE 也支持 INTO DUMPFILE。如果表包含多行,您还必须使用 LIMIT 1 将输出限制为单行。INTO DUMPFILE 也可以与
SELECT * FROM (VALUES ROW()[, ...]) AS table_alias [LIMIT 1] 一起使用。参见第 15.2.19 节“VALUES 语句”。
注意
由 INTO OUTFILE 或 INTO DUMPFILE 创建的任何文件都归运行 mysqld
的操作系统用户所有。(出于此原因及其他原因,您绝不应以 root 身份运行 mysqld。)从 MySQL 8.0.17 开始,创建文件的
umask 为 0640;您必须拥有足够的访问权限来操作文件内容。在 MySQL 8.0.17 之前,umask 为
0666,文件可由服务器主机上的所有用户写入。
如果 secure_file_priv 系统变量设置为非空目录名,则要写入的文件必须位于该目录中。
在作为事件调度程序执行的事件一部分的 SELECT … INTO 语句上下文中,诊断消息(不仅是错误,还包括警告)会写入错误日志,在 Windows 上还会写入应用程序事件日志。更多信息,请参见第 27.4.5 节“事件调度程序状态”。
从 MySQL 8.0.22 开始,支持对 SELECT INTO OUTFILE 和 SELECT INTO DUMPFILE
写入的输出文件进行定期同步,通过设置该版本引入的 select_into_disk_sync 服务器系统变量启用。可以分别使用
select_into_buffer_size 和 select_into_disk_sync_delay
设置输出缓冲区大小和可选延迟。更多信息,请参见这些系统变量的描述。
20.SELECT语句STRAIGHT_JOIN、SQL_SMALL_RESULT
-
SELECT语句中的选项,例如STRAIGHT_JOIN或SQL_SMALL_RESULT。
在 SELECT 关键字之后,您可以使用多个修饰符来影响语句的操作。HIGH_PRIORITY、STRAIGHT_JOIN 以及以 SQL_ 开头的修饰符是 MySQL 对标准 SQL 的扩展。
STRAIGHT_JOIN 强制优化器按照 FROM 子句中列出的顺序来连接表。如果优化器以非最优顺序连接表,您可以使用此修饰符来加速查询。STRAIGHT_JOIN 也可用于 table_references 列表中。参见第 15.2.13.2 节“JOIN 子句”。
STRAIGHT_JOIN 与 JOIN 类似,不同之处在于左表总是在右表之前被读取。这可用于那些(少数)连接优化器以非最优顺序处理表的情况。
21.无需在GROUP BY子句中列出所有选定的列
- 您无需在
GROUP BY子句中列出所有选定的列。这对于某些非常特殊但相当正常的查询可以提高性能。参见第 14.19 节,“聚合函数”。
22.对GROUP BY指定ASC和DESC
- 您可以对
GROUP BY指定ASC和DESC,而不仅限于ORDER BY。
Deprecation of the syntax GROUP BY ASC/DESC in MySQL 5.7.
23.使用:=赋值运算符
- 能够在语句中使用
:=赋值运算符设置变量。参见第 11.4 节,“用户定义变量”。
您可以在一个语句中将值存储到用户定义变量中,并在另一个语句中引用它。这使您能够将值从一个语句传递到另一个语句。
用户变量写作 @var_name,其中变量名 var_name 由字母数字字符、.、_ 和 $ 组成。如果以字符串或标识符引用,用户变量名可以包含其他字符(例如,@‘my-var’、@“my-var” 或 @`my-var`)。
用户定义变量是会话特定的。一个客户端定义的用户变量不能被其他客户端看到或使用。(例外:有权访问 Performance Schema user_variables_by_thread 表的用户可以查看所有会话的所有用户变量。)当客户端退出时,给定客户端会话的所有变量都会自动释放。
用户变量名不区分大小写。名称的最大长度为 64 个字符。
设置用户定义变量的一种方法是发出 SET 语句:SET @var_name = expr [, @var_name = expr] … 对于 SET,= 或 := 都可以用作赋值运算符。
用户变量可以分配以下有限数据类型之一的值:整数、小数、浮点数、二进制或非二进制字符串,或 NULL 值。对小数和实数值的赋值不保留值的精度或标度。非允许类型的值将转换为允许的类型。例如,具有时间或空间数据类型的值被转换为二进制字符串。具有 JSON 数据类型的值被转换为字符集为 utf8mb4、排序规则为 utf8mb4_bin 的字符串。
如果为用户变量分配了非二进制(字符)字符串值,则它具有与字符串相同的字符集和排序规则。用户变量的可强制性是隐式的。(这与表列值的可强制性相同。)
分配给用户变量的十六进制或位值被视为二进制字符串。要将十六进制或位值作为数字分配给用户变量,请在数值上下文中使用它。例如,加 0 或使用 CAST(… AS UNSIGNED):
mysql> SET @v1 = X'41'; mysql> SET @v2 = X'41'+0; mysql> SET @v3 = CAST(X'41' AS UNSIGNED); mysql> SELECT @v1, @v2, @v3; +------+------+------+ | @v1 | @v2 | @v3 | +------+------+------+ | A | 65 | 65 | +------+------+------+ mysql> SET @v1 = b'1000001'; mysql> SET @v2 = b'1000001'+0; mysql> SET @v3 = CAST(b'1000001' AS UNSIGNED); mysql> SELECT @v1, @v2, @v3; +------+------+------+ | @v1 | @v2 | @v3 | +------+------+------+ | A | 65 | 65 | +------+------+------+
如果在结果集中选择用户变量的值,它将作为字符串返回给客户端。
如果引用未初始化的变量,则其值为 NULL,类型为字符串。
从 MySQL 8.0.22 开始,预处理语句中对用户变量的引用在首 次准备语句时确定其类型,并在以后每次执行该语句时保留此类型。同样,存储过程内的语句中使用的用户变量的类型在首 次调用存储过程时确定,并在每次后续调用时保留此类型。
用户变量可以在大多数允许表达式的上下文中使用。目前不包括明确要求字面值的上下文,例如 SELECT 语句的 LIMIT 子句或 LOAD DATA 语句的 IGNORE N LINES 子句。
早期版本的 MySQL 允许在 SET 以外的语句中为用户变量赋值。MySQL 8.0 支持此功能以保持向后兼容性,但可能在未来的 MySQL 版本中移除。
以这种方式赋值时,必须使用 := 作为赋值运算符;在 SET 以外的语句中,= 被视为比较运算符。
涉及用户变量的表达式求值顺序未定义。例如,不能保证 SELECT @a, @a:=@a+1 先求值 @a 然后执行赋值。
此外,变量的默认结果类型基于其在语句开始时的类型。如果变量在语句开始时持有一种类型的值,而在同一语句中又被赋予另一种类型的新值,则可能会产生意外效果。
为避免此行为带来的问题,要么不要在单个语句中为同一变量赋值并读取其值,要么在使用变量之前将其设置为 0、0.0 或 ‘’ 以定义其类型。
当 HAVING、GROUP BY 和 ORDER BY 子句引用在选择表达式列表中赋值的变量时,可能无法按预期工作,因为表达式在客户端求值,因此可能使用前一行的陈旧列值。
用户变量用于提供数据值。它们不能直接在 SQL 语句中用作标识符或标识符的一部分,例如在需要表名或数据库名的上下文中,或作为保留字(如 SELECT)。即使变量被引用也是如此,如下例所示:
mysql> SELECT c1 FROM t; +----+ | c1 | +----+ | 0 | +----+ | 1 | +----+ 2 rows in set (0.00 sec) mysql> SET @col = "c1"; Query OK, 0 rows affected (0.00 sec) mysql> SELECT @col FROM t; +------+ | @col | +------+ | c1 | +------+ 1 row in set (0.00 sec) mysql> SELECT \`@col\` FROM t; ERROR 1054 (42S22): Unknown column '@col' in 'field list' mysql> SET @col = "\`c1\`"; Query OK, 0 rows affected (0.00 sec) mysql> SELECT @col FROM t; +------+ | @col | +------+ | \`c1\` | +------+ 1 row in set (0.00 sec)
用户变量不能用于提供标识符这一原则有一个例外,即当您构建一个字符串作为预处理语句以供稍后执行时。在这种情况下,用户变量可用于提供语句的任何部分。以下示例说明了如何实现:
mysql> SET @c = "c1";
Query OK, 0 rows affected (0.00 sec)
mysql> SET @s = CONCAT("SELECT ", @c, " FROM t");
Query OK, 0 rows affected (0.00 sec)
mysql> PREPARE stmt FROM @s;
Query OK, 0 rows affected (0.04 sec)
Statement prepared
mysql> EXECUTE stmt;
+----+
| c1 |
+----+
| 0 |
+----+
| 1 |
+----+
2 rows in set (0.00 sec)
mysql> DEALLOCATE PREPARE stmt;
Query OK, 0 rows affected (0.00 sec)
更多信息,请参见第 15.5 节“预处理语句”。
类似的技术可用于应用程序中,使用程序变量构建 SQL 语句,如下所示使用 PHP 5:
query($query);
while($row = $result->fetch_assoc())
{
echo "" . $row["$col"] . "
\n";
}
$result->close();
$mysqli->close();
?>
以这种方式组装 SQL 语句有时称为“动态 SQL”。
数据类型
01.MEDIUMINT、SET、ENUM等
-
MEDIUMINT、SET和ENUM数据类型,以及各种BLOB和TEXT数据类型。
mysql> create table cjc.t5(id MEDIUMINT,c1 set('1','2','3'),c2 enum('1','2','3'));
Query OK, 0 rows affected (0.31 sec)
mysql> insert into cjc.t5 values(1,0,0);
ERROR 1265 (01000): Data truncated for column 'c2' at row 1
mysql> insert into cjc.t5 values(1,1,1);
Query OK, 1 row affected (0.01 sec)
02.AUTO_INCREMENT、BINARY等
-
AUTO_INCREMENT、BINARY、NULL、UNSIGNED和ZEROFILL数据类型属性。
mysql> create table t6(id int primary key auto_increment,name varchar(10)); Query OK, 0 rows affected (0.37 sec)
函数和运算符
01.函数别名
- 为方便从其他 SQL 环境迁移的用户,MySQL 服务器支持许多函数的别名。例如,所有字符串函数都支持标准 SQL 语法和 ODBC 语法。
02.
-
MySQL 服务器将
||和&&运算符理解为逻辑 OR 和 AND,就像在 C 编程语言中一样。在 MySQL 服务器中,||和OR是同义词,&&和AND也是同义词。由于这种简洁的语法,MySQL 服务器不支持标准 SQL 的||字符串连接运算符;请改用CONCAT()。由于CONCAT()接受任意数量的参数,因此很容易将||运算符的使用转换到 MySQL 服务器。 -
使用
COUNT(DISTINCT value_list),其中value_list包含多个元素。 -
字符串比较在默认情况下是 不区分大小写的,排序顺序由当前字符集的排序规则决定(默认为
utf8mb4)。要执行 区分大小写的比较,您应该使用BINARY属性声明列,或使用BINARY转换,这将导致比较使用底层字符编码值而不是词法顺序进行。 -
%运算符是MOD()的同义词。即,N % M等价于MOD(N,M)。支持%是为了方便 C 程序员和与 PostgreSQL 兼容。 -
在
SELECT语句的输出列列表(FROM左侧)的表达式中,可以使用=、<>、<=、<、>=、>、<<、>>、<=>、AND、OR或LIKE运算符。例如:
mysql> SELECT col1=1 AND col2=2 FROM my_table; -
LAST_INSERT_ID()函数返回最近的AUTO_INCREMENT值。参见第 14.15 节,“信息函数”。 -
允许对数值使用
LIKE。 -
REGEXP和NOT REGEXP扩展正则表达式运算符。 -
带一个参数或超过两个参数的
CONCAT()或CHAR()。(在 MySQL 服务器中,这些函数可以接受可变数量的参数。) -
BIT_COUNT()、CASE、ELT()、FROM_DAYS()、FORMAT()、IF()、MD5()、PERIOD_ADD()、PERIOD_DIFF()、TO_DAYS()和WEEKDAY()函数。 -
使用
TRIM()修剪子字符串。标准 SQL 仅支持删除单个字符。 -
GROUP BY函数STD()、BIT_OR()、BIT_AND()、BIT_XOR()和GROUP_CONCAT()。参见第 14.19 节,“聚合函数”。
UPDATE 的差异
如果在表达式中访问要更新的表中的列,
UPDATE 会使用该列的
当前值。以下语句中的第二个赋值操作会将
col2 设置为
col1 的当前(更新后的)值,而不是
col1 的原始值。结果是
col1 和
col2 具有相同的值。此行为与标准 SQL 不同。
mysql> create table cjc.t10(id int,col1 int,col2 int); Query OK, 0 rows affected (0.07 sec) mysql> insert into cjc.t10 values(1,10,100),(2,20,200); sQuery OK, 2 rows affected (0.30 sec) Records: 2 Duplicates: 0 Warnings: 0 mysql> select * from cjc.t10; +------+------+------+| id | col1 | col2 | +------+------+------+| 1 | 10 | 100 | | 2 | 20 | 200 | +------+------+------+2 rows in set (0.00 sec) mysql> UPDATE cjc.t10 SET col1 = col1 + 1, col2 = col1; Query OK, 2 rows affected (0.01 sec) Rows matched: 2 Changed: 2 Warnings: 0 mysql> select * from cjc.t10; +------+------+------+| id | col1 | col2 | +------+------+------+| 1 | 11 | 11 | | 2 | 21 | 21 | +------+------+------+2 rows in set (0.00 sec)
参考:
https://dev.mysql.com/doc/refman/8.0/en/extensions-to-ansi.html https://dev.mysql.com/doc/refman/8.0/en/analyze-table.html https://dev.mysql.com/doc/refman/8.0/en/check-table.html https://dev.mysql.com/doc/refman/8.0/en/checksum-table.html https://dev.mysql.com/doc/refman/8.0/en/optimize-table.html https://dev.mysql.com/doc/refman/8.0/en/repair-table.html https://dev.mysql.com/doc/refman/8.0/en/create-database.html https://dev.mysql.com/doc/refman/8.0/en/drop-database.html https://dev.mysql.com/doc/refman/8.0/en/alter-database.html https://dev.mysql.com/doc/refman/8.0/en/do.html https://dev.mysql.com/doc/refman/8.0/en/explain.html https://dev.mysql.com/doc/refman/8.0/en/flush.html https://dev.mysql.com/doc/refman/8.0/en/reset.html https://dev.mysql.com/doc/refman/8.0/en/set-variable.html https://dev.mysql.com/doc/refman/8.0/en/show.html https://dev.mysql.com/doc/refman/8.0/en/load-data.html https://dev.mysql.com/doc/refman/8.0/en/rename-table.html https://dev.mysql.com/doc/refman/8.0/en/replace.html https://dev.mysql.com/doc/refman/8.0/en/alter-table.html https://dev.mysql.com/doc/refman/8.0/en/create-table.html https://dev.mysql.com/doc/refman/8.0/en/drop-table.html https://dev.mysql.com/doc/refman/8.0/en/update.html https://dev.mysql.com/doc/refman/8.0/en/select.html https://dev.mysql.com/doc/refman/8.0/en/select-into.html https://dev.mysql.com/doc/refman/8.0/en/user-variables.html https://dev.mysql.com/doc/refman/8.0/en/ansi-diff-update.html
欢迎关注我的公众号《 IT小Chen》