ThinkChat2.0新版上线,更智能更精彩,支持会话、画图、阅读、搜索等,送10W Token,即刻开启你的AI之旅 广告
### 第13章:SQL语句语法 ** 目录** [ 13.1. 数据定义语句](#)[13.1.1. ALTER DATABASE语法](#)[13.1.2. ALTER TABLE语法](#)[13.1.3. CREATE DATABASE语法](#)[13.1.4. CREATE INDEX语法](#)[13.1.5. CREATE TABLE语法](#)[13.1.6. DROP DATABASE语法](#)[13.1.7. DROP INDEX语法](#)[13.1.8. DROP TABLE语法](#)[13.1.9. RENAME TABLE语法](#)[ 13.2. 数据操作语句](#)[13.2.1. DELETE语法](#)[13.2.2. DO语法](#)[13.2.3. HANDLER语法](#)[13.2.4. INSERT语法](#)[13.2.5. LOAD DATA INFILE语法](#)[13.2.6. REPLACE语法](#)[13.2.7. SELECT语法](#)[ 13.2.8. Subquery语法](#)[13.2.9. TRUNCATE语法](#)[13.2.10. UPDATE语法](#)[ 13.3. MySQL实用工具语句](#)[13.3.1. DESCRIBE语法(获取有关列的信息)](#)[13.3.2. USE语法](#)[ 13.4. MySQL事务处理和锁定语句](#)[13.4.1. START TRANSACTION, COMMIT和ROLLBACK语法](#)[ 13.4.2. 不能回滚的语句](#)[ 13.4.3. 会造成隐式提交的语句](#)[13.4.4. SAVEPOINT和ROLLBACK TO SAVEPOINT语法](#)[13.4.5. LOCK TABLES和UNLOCK TABLES语法](#)[13.4.6. SET TRANSACTION语法](#)[ 13.4.7. XA事务](#)[ 13.5. 数据库管理语句](#)[ 13.5.1. 账户管理语句](#)[ 13.5.2. 表维护语句](#)[13.5.3. SET语法](#)[13.5.4. SHOW语法](#)[ 13.5.5. 其它管理语句](#)[ 13.6. 复制语句](#)[ 13.6.1. 用于控制主服务器的SQL语句](#)[ 13.6.2. 用于控制从服务器的SQL语句](#)[ 13.7. 用于预处理语句的SQL语法](#) 本章介绍了SQL语句的语法。 ### 13.1. 数据定义语句 [13.1.1. ALTER DATABASE语法](#)[13.1.2. ALTER TABLE语法](#)[13.1.3. CREATE DATABASE语法](#)[13.1.4. CREATE INDEX语法](#)[13.1.5. CREATE TABLE语法](#)[13.1.6. DROP DATABASE语法](#)[13.1.7. DROP INDEX语法](#)[13.1.8. DROP TABLE语法](#)[13.1.9. RENAME TABLE语法](#) ### 13.1.1. ALTER DATABASE语法 ~~~ ALTER {DATABASE | SCHEMA} [db_name] alter_specification [, alter_specification] ... alter_specification: [DEFAULT] CHARACTER SET charset_name | [DEFAULT] COLLATE collation_name ~~~ ALTER DATABASE用于更改数据库的全局特性。这些特性储存在数据库目录中的db.opt文件中。要使用ALTER DATABASE,您需要获得数据库ALTER权限。 CHARACTER SET子句用于更改默认的数据库字符集。COLLATE子句用于更改默认的数据库整序。在[第10章](#):[*字符集支持*](# "Chapter 10. Character Set Support")中对字符集和整序名称进行了讨论。 数据库名称可以忽略,此时,语句对应于默认数据库。也可以使用ALTER SCHEMA。 ### 13.1.2. ALTER TABLE语法 ~~~ ALTER [IGNORE] TABLE tbl_name alter_specification [, alter_specification] ... alter_specification: ADD [COLUMN] column_definition [FIRST | AFTER col_name ] | ADD [COLUMN] (column_definition,...) | ADD INDEX [index_name] [index_type] (index_col_name,...) | ADD [CONSTRAINT [symbol]] PRIMARY KEY [index_type] (index_col_name,...) | ADD [CONSTRAINT [symbol]] UNIQUE [index_name] [index_type] (index_col_name,...) | ADD [FULLTEXT|SPATIAL] [index_name] (index_col_name,...) | ADD [CONSTRAINT [symbol]] FOREIGN KEY [index_name] (index_col_name,...) [reference_definition] | ALTER [COLUMN] col_name {SET DEFAULT literal | DROP DEFAULT} | CHANGE [COLUMN] old_col_name column_definition [FIRST|AFTER col_name] | MODIFY [COLUMN] column_definition [FIRST | AFTER col_name] | DROP [COLUMN] col_name | DROP PRIMARY KEY | DROP INDEX index_name | DROP FOREIGN KEY fk_symbol | DISABLE KEYS | ENABLE KEYS | RENAME [TO] new_tbl_name | ORDER BY col_name | CONVERT TO CHARACTER SET charset_name [COLLATE collation_name] | [DEFAULT] CHARACTER SET charset_name [COLLATE collation_name] | DISCARD TABLESPACE | IMPORT TABLESPACE | table_options | partition_options | ADD PARTITION partition_definition | DROP PARTITION partition_names | COALESCE PARTITION number | REORGANIZE PARTITION partition_names INTO (partition_definitions) | ANALYZE PARTITION partition_names | CHECK PARTITION partition_names | OPTIMIZE PARTITION partition_names | REBUILD PARTITION partition_names | REPAIR PARTITION partition_names ~~~ ALTER TABLE用于更改原有表的结构。例如,您可以增加或删减列,创建或取消索引,更改原有列的类型,或重新命名列或表。您还可以更改表的评注和表的类型。 允许进行的变更中,许多子句的语法与CREATE TABLE中的子句的语法相近。其中包括*table_options*修改,选项有ENGINE, AUTO_INCREMENT和AVG_ROW_LENGTH等。请见[13.1.5节,“CREATE TABLE语法”](# "13.1.5. CREATE TABLE Syntax")。 存储引擎不支持有些操作,如果进行这些操作,会出现警告。使用SHOW WARNINGS可以显示出这些警告。请参见[13.5.4.22节,“SHOW WARNINGS语法”](# "13.5.4.22. SHOW WARNINGS Syntax")。 如果您使用ALTER TABLE更改列规约,但是DESCRIBE*tbl_name*提示您列规约并没有改变,则可能是因为MySQL忽略了您所做的更改。忽略更改的原因见[13.1.5.1节,“沉寂的列规格变更”](# "13.1.5.1. Silent Column Specification Changes")。例如,如果您试图把VARCHAR列更改为CHAR列,此时,如果表包含其它长度可变的列,则MySQL仍会使用VARCHAR。 ALTER TABLE运行时会对原表进行临时复制,在副本上进行更改,然后删除原表,再对新表进行重命名。在执行ALTER TABLE时,其它用户可以阅读原表,但是对表的更新和修改的操作将被延迟,直到新表生成为止。新表生成后,这些更新和修改信息会自动转移到新表上。 注意,如果您在执行ALTER TABLE时使用除了RENAME以外的选项,则MySQL会创建一个临时表。即使数据并不需要进行复制(例如当您更改列的名称时),MySQL也会这么操作。对于MyISAM表,您可以通过把myisam_sort_buffer_size系统变量设置到一个较高的值,来加快重新创建索引(该操作是变更过程中速度最慢的一部分)的速度。 ·         要使用ALTER TABLE,您需要获得表的ALTER, INSERT和CREATE权限。 ·         IGNORE是MySQL相对于标准SQL的扩展。如果在新表中有重复关键字,或者当STRICT模式启动后出现警告,则使用IGNORE控制ALTER TABLE的运行。如果没有指定IGNORE,当重复关键字错误发生时,复制操作被放弃,返回前一步骤。如果指定了IGNORE,则对于有重复关键字的行,只使用第一行,其它有冲突的行被删除。并且,对错误值进行修正,使之尽量接近正确值。 ·         您可以在一个ALTER TABLE语句里写入多个ADD, ALTER, DROP和CHANGE子句,中间用逗号分开。这是MySQL相对于标准SQL的扩展。在标准SQL中,每个ALTER TABLE语句中每个子句只允许使用一次。例如,在一个语句中取消多个列: ~~~ ·                mysql> ALTER TABLE t2 DROP COLUMN c, DROP COLUMN d; ~~~ ·         CHANGE *col_name*, DROP *col_name*和DROP INDEX是MySQL相对于标准SQL的扩展。 ·         MODIFY是Oracle对ALTER TABLE的扩展。 ·         COLUMN只是自选项目,可以忽略。 ·         如果您使用ALTER TABLE *tbl_name* RENAME TO *new_tbl_name*并且没有其它选项,则MySQL只对与table *tbl_name*相对应的文件进行重命名。不需要创建一个临时表。(您也可以使用RENAME TABLE语句对表进行重命名。请参见[13.1.9节,“RENAME TABLE语法”](# "13.1.9. RENAME TABLE Syntax")。) ·         * column_definition*子句使用与CREATE TABLE中的ADD和CHANGE子句相同的语法。注意,此语法包括列名称,而不只是列类型。请参见[13.1.5节,“CREATE TABLE语法”](# "13.1.5. CREATE TABLE Syntax")。 ·         您可以使用CHANGE *old_col_name**column_definition*子句对列进行重命名。重命名时,需给定旧的和新的列名称和列当前的类型。例如:要把一个INTEGER列的名称从a变更到b,您需要如下操作: ~~~ ·                mysql> ALTER TABLE t1 CHANGE a b INTEGER; ~~~ 如果您想要更改列的类型而不是名称, CHANGE语法仍然要求旧的和新的列名称,即使旧的和新的列名称是一样的。例如: ~~~ mysql> ALTER TABLE t1 CHANGE b b BIGINT NOT NULL; ~~~ 您也可以使用MODIFY来改变列的类型,此时不需要重命名: ~~~ mysql> ALTER TABLE t1 MODIFY b BIGINT NOT NULL; ~~~ ·         如果您使用CHANGE或MODITY缩短列长时,列中存在有索引,并且缩短后的列长小于索引长度,则MySQL会自动缩短索引的长度。 ·         当您使用CHANGE或MODIFY更改列的类型时,MySQL会尽量把原有的列值转化为新的类型。 ·         您可以使用FIRST或AFTER *col_name*在一个表行中的某个特定位置添加列。默认把列添加到最后。您也可以在CHANGE或MODIFY语句中使用FIRST和AFTER。 ·         AFTER COLUMN用于指定列的新默认值,或删除旧的默认值。如果旧的默认值被删除同时列值为NULL,则新的默认值为NULL。如果列值不能为NULL,MySQL会指定一个默认值,请参见[13.1.5节,“CREATE TABLE语法”](# "13.1.5. CREATE TABLE Syntax")。 ·         DROP INDEX用于取消索引。这是MySQL相对于标准SQL的扩展。请参见[13.1.7节,“DROP INDEX语法”](# "13.1.7. DROP INDEX Syntax")。 ·         如果列从表中被取消了,则这些列也从相应的索引中被取消。如果组成一个索引的所有列均被取消,则该索引也被取消。 ·         如果一个表只包含一列,则此列不能被取消。如果您想要取消表,应使用DROP TABLE。 ·         DROP PRIMAY DEY用于取消主索引。注释:在MySQL较早的版本中,如果没有主索引,则DROP PRIMARY KEY会取消表中的第一个UNIQUE索引。在MySQL 5.1中不会出现这种情况。如果在MySQL 5.1中对没有主键的表使用DROP PRIMARY KEY,则会出现错误信息。 如果您向表中添加UNIQUE KEY或PRIMARY KEY,则UNIQUE KEY或PRIMARY KEY会被储存在非唯一索引之前,这样MySQL就可以尽早地检查出重复关键字。 ·         ORDER BY用于在创建新表时,让各行按一定的顺序排列。注意,在插入和删除后,表不会仍保持此顺序。当您知道多数情况下您会按照特定的顺序查询各行时,可以使用这个选项;在对表进行了大的改动后,通过使用此选项,您可以提高查询效率。在有些情况下,如果表按列排序,对于MySQL来说,排序可能会更简单。 ·         如果您对一个MyISAM表使用ALTER TABLE,则所有非唯一索引会被创建到一个单独的批里(和REPAIR TABLE相同)。当您有许多索引时,这样做可以使ALTER TABLE的速度更快。 这项功能可以明确激活。ALTER TABLE...DISABLE KEYS让MySQL停止更新MyISAM表中的非唯一索引。然后使用ALTER TABLE ... ENABLE KEYS重新创建丢失的索引。进行此操作时,MySQL采用一种特殊的算法,比一个接一个地插入关键字要快很多。因此,在进行成批插入操作前先使关键字禁用可以大大地加快速度。使用ALTER TABLE ... DISABLE KEYS除了需要获得以前提到的权限以外,还需要获得INDEX权限。 ·         Innodb存储引擎支持FOREIGN KEY和REFERENCES子句。Innodb存储引擎执行ADD [CONSTRAINT [*symbol*]] FOREIGN KEY (...) REFERENCES ... (...)。请参见[15.2.6.4节,“FOREIGN KEY约束”](# "15.2.6.4. FOREIGN KEY Constraints")。对于其它存储引擎,这些子句会被分析,但是会被忽略。对于所有的存储引擎,CHECK子句会被分析,但是会被忽略。请参见[13.1.5节,“CREATE TABLE语法”](# "13.1.5. CREATE TABLE Syntax")。接受这些子句但又忽略子句的原因是为了提高兼容性,以便更容易地从其它SQL服务器中导入代码,并运行应用程序,创建带参考数据的表。请参见[1.8.5节,“MySQL与标准SQL的差别”](# "1.8.5. MySQL Differences from Standard SQL")。 ·         InnoDB支持使用ALTER TABLE来取消外键: ~~~ ·                ALTER TABLE yourtablename DROP FOREIGN KEY fk_symbol; ~~~ 要了解更多信息,请参见[15.2.6.4节,“FOREIGN KEY约束”](# "15.2.6.4. FOREIGN KEY Constraints")。 ·         ALTER TABLE忽略DATA DIRECTORY和INDEX DIRECTORY表选项。 ·         如果您想要把表默认的字符集和所有字符列(CHAR, VARCHAR, TEXT)改为新的字符集,应使用如下语句: ~~~ ·                ALTER TABLE tbl_name CONVERT TO CHARACTER SET charset_name; ~~~ 警告:前面的操作转换了字符集之间的列类型。如果您有一列使用一种字符集(如latin1),但是存储的值实际上使用了其它的字符集(如utf8),这种情况不是您想要的。此时,您必须对这样的列进行以下操作。 ~~~ ALTER TABLE t1 CHANGE c1 c1 BLOB; ~~~ ~~~ ALTER TABLE t1 CHANGE c1 c1 TEXT CHARACTER SET utf8; ~~~ 这种方法能够实现此功能的原因是,当您转换到BLOB列或从BLOB列转换过来时,并没有发生转换。 如果您指定CONVERT TO CHARACTER SET为二进制,则TEXT列被转换到相应的二进制字符串类型(BINARY, VARBINARY, BLOB)。这意味着这些列将不再有字符集,接下来的CONVERT TO操作也将不适用于这些列。 要仅仅改变一个表的默认字符集,应使用此语句: ~~~ ALTER TABLE tbl_name DEFAULT CHARACTER SET charset_name; ~~~ 词语DEFAULT为自选项。如果您在向表中添加一个新列时(例如,使用ALTER TABLE...ADD column)没有指定字符集,则此时使用的字符集为默认字符集。 警告:ALTER TABLE...DEFAULT CHARACTER SET和ALTER TABLE...CHARACTER SET是等价的,只用于更改默认的表字符集。 ·         如果InnoDB表在创建时,使用了.ibd文件中的自己的表空间,则这样的文件可以被删除和导入。使用此语句删除.ibd文件: ~~~ ·                ALTER TABLE tbl_name DISCARD TABLESPACE; ~~~ 此语句用于删除当前的.ibd文件,所以应首先确认您有一个备份。如果在表空间被删除后尝试打开表格,则会出现错误。 要把备份的.ibd文件还原到表中,需把此文件复制到数据库目录中,然后书写此语句: ~~~ ALTER TABLE tbl_name IMPORT TABLESPACE; ~~~ 见[15.2.6.6节,“使用按表的表空间”](# "15.2.6.6. Using Per-Table Tablespaces")。 ·         使用mysql_info() C API函数,您可以了解有多少记录已被复制,以及(当使用IGNORE时)有多少记录由于重复关键字的原因已被删除。请参见[25.2.3.34节,“mysql_info()”](# "25.2.3.34. mysql_info()")。 ·         ALTER TABLE也可以用于对带分区的表进行重新分区,功能包括添加、取消、合并和拆分各分区,还可以用于进行分区维护。 对带分区的表使用*partition_options*子句和ALTER TABLE可以对表进行重新分区,使用时依据*partition_options*定义的分区方法。本子句以PARTITION BY为开头,然后使用与用于CREATE TABLE的*partition_options*子句一样的语法和规则(要了解详细信息,请参见[13.1.5节,“CREATE TABLE语法”](# "13.1.5. CREATE TABLE Syntax"))。注释:MySQL 5.1服务器目前接受此语法,但是不实际执行;等MySQL 5.1开发出来后,将执行此语法。 用于ALTER TABLE ADD PARTITION的*partition_definition*子句支持用于CREATE TABLE语句的*partition_definition*子句的同样名称的选项。(要了解语法和介绍,请参见[13.1.5节,“CREATE TABLE语法”](# "13.1.5. CREATE TABLE Syntax")。)例如,假设您有一个按照以下方式创建的带分区的表: ~~~ CREATE TABLE t1 ( ~~~ ~~~     id INT, ~~~ ~~~     year_col INT ~~~ ~~~ ) ~~~ ~~~ PARTITION BY RANGE (year_col) ( ~~~ ~~~     PARTITION p0 VALUES LESS THAN (1991), ~~~ ~~~     PARTITION p1 VALUES LESS THAN (1995), ~~~ ~~~     PARTITION p2 VALUES LESS THAN (1999) ~~~ ~~~ );    ~~~ 您可以在表中增加一个新的分区p3,该分区用于储存小于2002的值。添加方法如下: ~~~ ALTER TABLE t1 ADD PARTITION p3 VALUES LESS THAN (2002); ~~~ 注释:您不能使用ALTER TABLE向一个没有进行分区的表添加分区。 DROP PARTITION用于取消一个或多个RANGE或LIST分区。此命令不能用于HASH或KEY 分区;用于这两个分区时,应使用COALESCE PARTITION(见后)。如果被取消的分区其名称列于*partition_names*清单中,则储存在此分区中的数据也被取消。例如,如果以前已定义的表t1,您可以采用如下方法取消名称为p0和p1的分区: ~~~ ALTER TABLE DROP PARTITION p0, p1; ~~~ ADD PARTITION和DROP PARTITION目前不支持IF [NOT] EXISTS。也不可能对一个分区或一个已分区的表进行重命名。如果您希望对一个分区进行重命名,您必须取消分区,再重新建立;如果您希望对一个已分区的表进行重新命名,您必须取消所有分区,然后对表进行重命名,再添加被取消的分区。 COALESCE PARTITION可以用于使用HASH或KEY进行分区的表,以便使用*number*来减少分区的数目。例如,假设您使用下列方法创建了表t2: ~~~ CREATE TABLE t2 ( ~~~ ~~~     name VARCHAR (30), ~~~ ~~~     started DATE ~~~ ~~~ ) ~~~ ~~~ PARTITION BY HASH(YEAR(started)) ~~~ ~~~ PARTITIONS (6); ~~~ 您可以使用以下命令,把t2使用的分区的数目由6个减少到4个: ~~~ ALTER TABLE t2 COALESCE PARTITION 2; ~~~ 包含在最后一个*number*分区中的数据将被合并到其余的分区中。在此情况下,分区4和分区5将被合并到前4个分区中(编号为0、1、2和3的分区)。 如果要更改部分分区,但不更改所有的分区,您可以使用REORGANIZE PARTITION。这个命令有多种使用方法: o        把多个分区合并为一个分区。通过把多个分区的名称列入*partition_names*清单,并为*partition_definition*提供一个单一的定义,可以实现这个功能。 o        把一个原有的分区拆分为多个分区。通过为*partition_names*命名一个分区,并提供多个*partition_definitions*,可以实现这个功能。 o        更改使用VALUES LESS THAN定义的分区子集的范围或更改使用VALUES IN定义的分区子集的值清单。 注释:对于没有明确命名的分区,MySQL会自动提供默认名称p0, p1, p2等。 要了解有关ALTER TALBE...REORANIZE PARTITION命令的详细信息,请参见[18.3节,“分区管理”](# "18.3. Partition Management")。 ·         多个附加子句用于提供分区维护和修补功能。这些功能与用于非分区表的功能类似。这些功能由CHECK TABLE和REPAIR TABLE等命令(这些命令不支持用于分区表)执行。这些子句包括ANALYZE PARTITION, CHECK PARTITION, OPTIMIZE PARTITION, REBUILD PARTITION和REPAIR PARTITION.每个选项均为一个*partition_names*子句,包括一个或多个分区名称。需要更改的表中必须已存在这些分区。多个分区名称用逗号分隔。要了解更多信息,或要了解举例说明,请参见[18.3.3节,“分区维护”](# "18.3.3. Maintenance of Partitions")。 以下例子展示了ALTER TABLE的使用。首先展示表t1。表t1采用如下方法创建: ~~~ mysql> CREATE TABLE t1 (a INTEGER,b CHAR(10)); ~~~ 把表t1重新命名为t2: ~~~ mysql> ALTER TABLE t1 RENAME t2; ~~~ 把列a从INTERGER更改为TINYINT NOT NULL(名称保持不变),并把列b从CHAR(10)更改为CHAR(20),同时把列b重新命名为列c: ~~~ mysql> ALTER TABLE t2 MODIFY a TINYINT NOT NULL, CHANGE b c CHAR(20); ~~~ 添加一个新的TIMESTAMP列,名称为d: ~~~ mysql> ALTER TABLE t2 ADD d TIMESTAMP; ~~~ 在列d和列a中添加索引: ~~~ mysql> ALTER TABLE t2 ADD INDEX (d), ADD INDEX (a); ~~~ 删除列c: ~~~ mysql> ALTER TABLE t2 DROP COLUMN c; ~~~ 添加一个新的AUTO_INCREMENT整数列,名称为c: ~~~ mysql> ALTER TABLE t2 ADD c INT UNSIGNED NOT NULL AUTO_INCREMENT, ~~~ ~~~     ->     ADD PRIMARY KEY (c); ~~~ 注意我们为c编制了索引(作为PRIMARY KEY),因为AUTO_INCREMENT列必须编制索引。同时我们定义c为NOT NULL,因为主键列不能为NULL。 当您添加一个AUTO_INCREMENT列时,列值被自动地按序号填入。对于MyISAM表,您可以在ALTER TABLE之前执行SET INSERT_ID=*value*来设置第一个序号,也可以使用AUTO_INCREMENT=*value*表选项来设置。请参见[13.5.3节,“SET语法”](# "13.5.3. SET Syntax")。 如果值大于AUTO_INCREMENT列中的最大值,则您可以使用用于InnoDB表的ALTER TALBE...AUTO_INCREMENT=*value*表选项,来为新行设置序号。如果值小于列中当前的最大值,不会出现错误信息,当前的序列值也不改变。 使用MyISAM表时,如果您不更改AUTO_INCREMENT列,则序列号不受影响。如果您取消一个AUTO_INCREMENT列,然后添加另一个AUTO_INCREMENT列,则序号重新排列,从1开始。 见[A.7.1节,“与ALTER TABLE有关的问题”](# "A.7.1. Problems with ALTER TABLE")。 ### 13.1.3. CREATE DATABASE语法 ~~~ CREATE {DATABASE | SCHEMA} [IF NOT EXISTS] db_name ~~~ ~~~     [create_specification [, create_specification] ...] ~~~ ~~~   ~~~ ~~~ create_specification: ~~~ ~~~     [DEFAULT] CHARACTER SET charset_name ~~~ ~~~   | [DEFAULT] COLLATE collation_name ~~~ CREATE DATABASE用于创建数据库,并进行命名。如果要使用CREATE DATABASE,您需要获得数据库CREATE权限。 有关合法数据库名称的规定列于[9.2节,“数据库、表、索引、列和别名”](# "9.2. Database, Table, Index, Column, and Alias Names")。如果存在数据库,并且您没有指定IF NOT EXISTS,则会出现错误。 create_specification选项用于指定数据库的特性。数据库特性储存在数据库目录中的db.opt文件中。CHARACTER SET子句用于指定默认的数据库字符集。COLLATE子句用于指定默认的数据库整序。字符集和整序名称在[第10章](#):[*字符集支持*](# "Chapter 10. Character Set Support")中讨论。 有些目录包含文件,这些文件与数据库中的表对应。MySQL中的数据库的执行方法与这些目录的执行方法相同。因为当数据库刚刚被创建时,在数据库中没有表,所以CREATE DATABASE只创建一个目录。这个目录位于MySQL数据目录和db.opt文件之下。 如果您手动在数据目录之下创建一个目录(例如,使用**mkdir**),则服务器会认为这是一个数据库目录,并在SHOW DATABASES的输出中显示出来。 也可以使用CREATE SCHEMA。 您还可以使用**mysqladmin**程序创建数据库。请参见[8.5节,“mysqladmin:用于管理MySQL服务器的客户端”](# "8.5. mysqladmin — Client for Administering a MySQL Server")。 ### 13.1.4. CREATE INDEX语法 ~~~ CREATE [UNIQUE|FULLTEXT|SPATIAL] INDEX index_name ~~~ ~~~     [USING index_type] ~~~ ~~~     ON tbl_name (index_col_name,...) ~~~ ~~~   ~~~ ~~~ index_col_name: ~~~ ~~~     col_name [(length)] [ASC | DESC] ~~~ CREATE INDEX被映射到一个ALTER TABLE语句上,用于创建索引。请参见[13.1.2节,“ALTER TABLE语法”](# "13.1.2. ALTER TABLE Syntax")。 通常,当使用CREATE TABLE创建表时,也同时在表中创建了所有的索引。请参见[13.1.5节,“CREATE TABLE语法”](# "13.1.5. CREATE TABLE Syntax")。CREATE INDEX允许您向已有的表中添加索引。 格式为(col1, col2,...)的一个列清单创建出一个多列索引。通过串接给定列中的值,确定索引值的格式。 对于CHAR和VARCHAR列,只用一列的一部分就可创建索引。创建索引时,使用*col_name*(*length*)语法,对前缀编制索引。前缀包括每列值的前*length*个字符。BLOB和TEXT列也可以编制索引,但是必须给出前缀长度。 此处展示的语句用于创建一个索引,索引使用列名称的前10个字符。 ~~~ CREATE INDEX part_of_name ON customer (name(10)); ~~~ 因为多数名称的前10个字符通常不同,所以此索引不会比使用列的全名创建的索引速度慢很多。另外,使用列的一部分创建索引可以使索引文件大大减小,从而节省了大量的磁盘空间,有可能提高INSERT操作的速度。 前缀最长为255字节。对于MyISAM和InnoDB表,前缀最长为1000字节。注意前缀的限长以字节计,而CREATE INDEX语句中的前缀长度指的是字符的数目。对于使用多字节字符集的列,在指定列的前缀长度时,要考虑这一点。 在MySQL 5.1中: ·         只有当您正在使用MyISAM, InnoDB或BDB表类型时,您可以向有NULL值的列中添加索引。 ·         只有当您正在使用MyISAM, BDB或InnoDB表类型时,您可以向BLOB或TEXT列中添加索引。 一个*index_col_name*规约可以以ASC或DESC为结尾。这些关键词将来可以扩展,用于指定递增或递减索引值存储。目前,这些关键词被分析,但是被忽略;索引值均以递增顺序存储。 部分储存引擎允许在创建索引时指定索引类型。*index_type*指定语句的语法是USING *type_name*。不同的储存引擎所支持的*type_name*值已显示在下表中。如果列有多个索引类型,当没有指定*index_type*时,第一个类型是默认值。 <table border="1" cellpadding="0" id="table1"><tr><td> <p><strong><span> 存储引擎</span></strong></p></td> <td> <p><strong><span> 允许的索引类型</span></strong></p></td> </tr><tr><td> <p> <span>MyISAM</span></p></td> <td> <p> <span>BTREE</span></p></td> </tr><tr><td> <p> <span>InnoDB</span></p></td> <td> <p> <span>BTREE</span></p></td> </tr><tr><td> <p> <span>MEMORY</span><span>/<span>HEAP</span></span></p></td> <td> <p> <span>HASH</span><span>, <span>BTREE</span></span></p></td> </tr></table> 示例: ~~~ CREATE TABLE lookup (id INT) ENGINE = MEMORY; ~~~ ~~~ CREATE INDEX id_index USING BTREE ON lookup (id); ~~~ TYPE *type_name*可以作为USING *type_name*的同义词,用于指定索引类型。但是,USING是首选的格式。另外,在索引规约语法中,位于索引类型前面的索引名称不能使用TYPE。这是因为,与USING不同,TYPE不是保留词,因此会被认为是一个索引名称。 如果您指定的索引类型在给定的储存引擎中不合法,但是有其它的索引类型适合引擎使用,并且不会影响查询功能,则引擎应使用此类型。 要了解更多有关MySQL如何使用索引的信息,请参见[7.4.5节,“MySQL如何使用索引”](# "7.4.5. How MySQL Uses Indexes")。 FULLTEXT索引只能对CHAR, VARCHAR和TEXT列编制索引,并且只能在MyISAM表中编制。请参见[12.7节,“全文搜索功能”](# "12.7. Full-Text Search Functions")。 SPATIAL索引只能对空间列编制索引,并且只能在MyISAM表中编制。空间列类型在[第19章:](#)[*MySQL中的空间扩展*](# "Chapter 19. Spatial Extensions in MySQL")中进行了描述。 ### 13.1.5. CREATE TABLE语法 [ 13.1.5.1. 沉寂的列规格变更](#) ~~~ CREATE [TEMPORARY] TABLE [IF NOT EXISTS] tbl_name ~~~ ~~~     [(create_definition,...)] ~~~ ~~~     [table_options] [select_statement] ~~~ 或: ~~~ CREATE [TEMPORARY] TABLE [IF NOT EXISTS] tbl_name ~~~ ~~~     [(] LIKE old_tbl_name [)]; ~~~ ~~~   ~~~ ~~~ create_definition: ~~~ ~~~     column_definition ~~~ ~~~   | [CONSTRAINT [symbol]] PRIMARY KEY [index_type] (index_col_name,...) ~~~ ~~~   | KEY [index_name] [index_type] (index_col_name,...) ~~~ ~~~   | INDEX [index_name] [index_type] (index_col_name,...) ~~~ ~~~   | [CONSTRAINT [symbol]] UNIQUE [INDEX] ~~~ ~~~         [index_name] [index_type] (index_col_name,...) ~~~ ~~~   | [FULLTEXT|SPATIAL] [INDEX] [index_name] (index_col_name,...) ~~~ ~~~   | [CONSTRAINT [symbol]] FOREIGN KEY ~~~ ~~~         [index_name] (index_col_name,...) [reference_definition] ~~~ ~~~   | CHECK (expr) ~~~ ~~~   ~~~ ~~~ column_definition: ~~~ ~~~     col_name type [NOT NULL | NULL] [DEFAULT default_value] ~~~ ~~~         [AUTO_INCREMENT] [UNIQUE [KEY] | [PRIMARY] KEY] ~~~ ~~~         [COMMENT 'string'] [reference_definition] ~~~ ~~~   ~~~ ~~~ type: ~~~ ~~~     TINYINT[(length)] [UNSIGNED] [ZEROFILL] ~~~ ~~~   | SMALLINT[(length)] [UNSIGNED] [ZEROFILL] ~~~ ~~~   | MEDIUMINT[(length)] [UNSIGNED] [ZEROFILL] ~~~ ~~~   | INT[(length)] [UNSIGNED] [ZEROFILL] ~~~ ~~~   | INTEGER[(length)] [UNSIGNED] [ZEROFILL] ~~~ ~~~   | BIGINT[(length)] [UNSIGNED] [ZEROFILL] ~~~ ~~~   | REAL[(length,decimals)] [UNSIGNED] [ZEROFILL] ~~~ ~~~   | DOUBLE[(length,decimals)] [UNSIGNED] [ZEROFILL] ~~~ ~~~   | FLOAT[(length,decimals)] [UNSIGNED] [ZEROFILL] ~~~ ~~~   | DECIMAL(length,decimals) [UNSIGNED] [ZEROFILL] ~~~ ~~~   | NUMERIC(length,decimals) [UNSIGNED] [ZEROFILL] ~~~ ~~~   | DATE ~~~ ~~~   | TIME ~~~ ~~~   | TIMESTAMP ~~~ ~~~   | DATETIME ~~~ ~~~   | CHAR(length) [BINARY | ASCII | UNICODE] ~~~ ~~~   | VARCHAR(length) [BINARY] ~~~ ~~~   | TINYBLOB ~~~ ~~~   | BLOB ~~~ ~~~   | MEDIUMBLOB ~~~ ~~~   | LONGBLOB ~~~ ~~~   | TINYTEXT [BINARY] ~~~ ~~~   | TEXT [BINARY] ~~~ ~~~   | MEDIUMTEXT [BINARY] ~~~ ~~~   | LONGTEXT [BINARY] ~~~ ~~~   | ENUM(value1,value2,value3,...) ~~~ ~~~   | SET(value1,value2,value3,...) ~~~ ~~~   | spatial_type ~~~ ~~~   ~~~ ~~~ index_col_name: ~~~ ~~~     col_name [(length)] [ASC | DESC] ~~~ ~~~   ~~~ ~~~ reference_definition: ~~~ ~~~     REFERENCES tbl_name [(index_col_name,...)] ~~~ ~~~                [MATCH FULL | MATCH PARTIAL | MATCH SIMPLE] ~~~ ~~~                [ON DELETE reference_option] ~~~ ~~~                [ON UPDATE reference_option] ~~~ ~~~   ~~~ ~~~ reference_option: ~~~ ~~~     RESTRICT | CASCADE | SET NULL | NO ACTION ~~~ ~~~   ~~~ ~~~ table_options: table_option [table_option] ... ~~~ ~~~   ~~~ ~~~ table_option: ~~~ ~~~     {ENGINE|TYPE} = engine_name ~~~ ~~~   | AUTO_INCREMENT = value ~~~ ~~~   | AVG_ROW_LENGTH = value ~~~ ~~~   | [DEFAULT] CHARACTER SET charset_name [COLLATE collation_name] ~~~ ~~~   | CHECKSUM = {0 | 1} ~~~ ~~~   | COMMENT = 'string' ~~~ ~~~   | CONNECTION = 'connect_string' ~~~ ~~~   | MAX_ROWS = value ~~~ ~~~   | MIN_ROWS = value ~~~ ~~~   | PACK_KEYS = {0 | 1 | DEFAULT} ~~~ ~~~   | PASSWORD = 'string' ~~~ ~~~   | DELAY_KEY_WRITE = {0 | 1} ~~~ ~~~   | ROW_FORMAT = {DEFAULT|DYNAMIC|FIXED|COMPRESSED|REDUNDANT|COMPACT} ~~~ ~~~   | UNION = (tbl_name[,tbl_name]...) ~~~ ~~~   | INSERT_METHOD = { NO | FIRST | LAST } ~~~ ~~~   | DATA DIRECTORY = 'absolute path to directory' ~~~ ~~~   | INDEX DIRECTORY = 'absolute path to directory' ~~~ ~~~   ~~~ ~~~ partition_options: ~~~ ~~~     PARTITION BY ~~~ ~~~            [LINEAR] HASH(expr) ~~~ ~~~         |  [LINEAR] KEY(column_list) ~~~ ~~~         |  RANGE(expr) ~~~ ~~~         |  LIST(column_list) ~~~ ~~~     [PARTITIONS num] ~~~ ~~~     [  SUBPARTITION BY ~~~ ~~~            [LINEAR] HASH(expr) ~~~ ~~~          | [LINEAR] KEY(column_list) ~~~ ~~~       [SUBPARTITIONS(num)]  ~~~ ~~~     ] ~~~ ~~~     [(partition_definition), [(partition_definition)], ...] ~~~ ~~~   ~~~ ~~~ partition_definition: ~~~ ~~~     PARTITION partition_name ~~~ ~~~         [VALUES { ~~~ ~~~                   LESS THAN (expr) | MAXVALUE ~~~ ~~~                 | IN (value_list) }] ~~~ ~~~         [[STORAGE] ENGINE [=] engine-name] ~~~ ~~~         [COMMENT [=] 'comment_text' ] ~~~ ~~~         [DATA DIRECTORY [=] 'data_dir'] ~~~ ~~~         [INDEX DIRECTORY [=] 'index_dir'] ~~~ ~~~         [MAX_ROWS [=] max_number_of_rows] ~~~ ~~~         [MIN_ROWS [=] min_number_of_rows] ~~~ ~~~         [TABLESPACE [=] (tablespace_name)] ~~~ ~~~         [NODEGROUP [=] node_group_id] ~~~ ~~~         [(subpartition_definition), [(subpartition_definition)], ...] ~~~ ~~~   ~~~ ~~~ subpartition_definition: ~~~ ~~~     SUBPARTITION logical_name ~~~ ~~~         [[STORAGE] ENGINE [=] engine-name] ~~~ ~~~         [COMMENT [=] 'comment_text' ] ~~~ ~~~         [DATA DIRECTORY [=] 'data_dir'] ~~~ ~~~         [INDEX DIRECTORY [=] 'index_dir'] ~~~ ~~~         [MAX_ROWS [=] max_number_of_rows] ~~~ ~~~         [MIN_ROWS [=] min_number_of_rows] ~~~ ~~~         [TABLESPACE [=] (tablespace_name)] ~~~ ~~~         [NODEGROUP [=] node_group_id] ~~~ ~~~   ~~~ ~~~ select_statement: ~~~ ~~~     [IGNORE | REPLACE] [AS] SELECT ...   (Some legal select statement) ~~~ CREATE TABLE用于创建带给定名称的表。您必须拥有表CREATE权限。 允许的表名称的规则列于[9.2节,“数据库、表、索引、列和别名”](# "9.2. Database, Table, Index, Column, and Alias Names")中。默认的情况是,表被创建到当前的数据库中。如果表已存在,或者如果没有当前数据库,或者如果数据库不存在,则会出现错误。 表名称被指定为*db_name.tbl_name*,以便在特定的数据库中创建表。不论是否有当前数据库,都可以通过这种方式创建表。如果您使用加引号的识别名,则应对数据库和表名称分别加引号。例如,`mydb`.`mytbl`是合法的,但是`mydb.mytbl`不合法。 在创建表格时,您可以使用TEMPORARY关键词。只有在当前连接情况下,TEMPORARY表才是可见的。当连接关闭时,TEMPORARY表被自动取消。这意味着两个不同的连接可以使用相同的临时表名称,同时两个临时表不会互相冲突,也不与原有的同名的非临时表冲突。(原有的表被隐藏,直到临时表被取消时为止。)您必须拥有CREATE TEMPORARY TABLES权限,才能创建临时表。 如果表已存在,则使用关键词IF NOT EXISTS可以防止发生错误。注意,原有表的结构与CREATE TABLE语句中表示的表的结构是否相同,这一点没有验证。注释:如果您在CREATE TABLE...SELECT语句中使用IF NOT EXISTS,则不论表是否已存在,由SELECT部分选择的记录都会被插入。 MySQL通过数据库目录中的.frm表格式(定义)文件表示每个表。表的存储引擎也可能会创建其它文件。对于MyISAM表,存储引擎可以创建数据和索引文件。因此,对于每个MyISAM表*tbl_name*,有三个磁盘文件: <table border="1" cellpadding="0" id="table2"><tr><td> <p><strong><span> 文件</span></strong></p></td> <td> <p><strong><span> 作用</span></strong></p></td> </tr><tr><td> <p><span><i> <span>tbl_name</span></i><span>.frm</span></span></p></td> <td> <p>表格式(定义)文件</p></td> </tr><tr><td> <p><span><i> <span>tbl_name</span></i><span>.MYD</span></span></p></td> <td> <p>数据文件</p></td> </tr><tr><td> <p><span><i> <span>tbl_name</span></i><span>.MYI</span></span></p></td> <td> <p>索引文件</p></td> </tr></table> 用于表示表的由存储引擎创建的文件在[第15章:*存储引擎和表类型*](# "Chapter 15. Storage Engines and Table Types")中描述。 要了解有关各种列类型的性质的一般说明,请参见[第11章:列类型](# "Chapter 11. Column Types")。要了解有关空间列类型的说明,请参见[第19章:](#)[*MySQL中的空间扩展*](# "Chapter 19. Spatial Extensions in MySQL")。 ·         如果没有指定是NULL或是NOT NULL,则列在创建时假定指定为NULL。 ·         一个整数列可以拥有一个附加属性AUTO_INCREMENT。当您向一个已编入索引的AUTO_INCREMENT列中插入一个NULL值(建议)或0时,此列被设置为下一个序列的值。通常情况下为*value*+1,此处*value*是当前在表中的列的最大值。AUTO_INCREMENT序列从1开始。这样的列必须被定义为一种整数类型,请参见[11.1.1节,“数值类型概述”](# "11.1.1. Overview of Numeric Types")中的叙述。(值1.0不是整数)。请参见[25.2.3.36节,“mysql_insert_id()”](# "25.2.3.36. mysql_insert_id()")。 为--sql-mode服务器选项或sql_mode系统变量指定NO_AUTO_VALUE_ON_ZERO特征位,这样可以把0存储到AUTO_INCREMENT列中,同时不生成一个新的序列值。请参见[5.3.1节,“**mysqld**命令行选项”](# "5.3.1. mysqld Command-Line Options")。 注释:有时候,每个表只有一个AUTO_INCREMENT列,此列必须编制索引,不能有DEFAULT值。一个AUTO_INCREMENT列只有在只包含正数的情况下,才能运行正常。插入一个负数会被认为是插入了一个非常大的正数。这样做是为了避免当数字由正数转为负数时出现精度问题,同时也为了确保AUTO_INCREMENT列中不会包含0。 对于MyISAM和BDB表,您可以在一个多列关键字中指定一个AUTO_INCREMENT次级列。请参见[3.6.9节,“使用AUTO_INCREMENT”](# "3.6.9. Using AUTO_INCREMENT")。 为了让MySQL与部分ODBC应用软件相兼容,您可以使用以下查询方法找到最后一个插入行的AUTO_INCREMENT值: ~~~ SELECT * FROM tbl_name WHERE auto_col IS NULL ~~~ ·         字符列的定义可以包括一个CHARACTER SET属性,用来指定字符集,也可以指定列的整序。要了解详细情况,请参见[第10章:*字符集支持*](# "Chapter 10. Character Set Support")。CHARSET是CHARACTER SET的同义词。 ~~~ ·                CREATE TABLE t (c CHAR(20) CHARACTER SET utf8 COLLATE utf8_bin); ~~~ MySQL 5.1理解,在字符列定义中的长度规约以字符为单位。(有些早期版本以字节为单位。) ·         DEFAULT子句用于为列指定一个默认值。默认值必须为一个常数,不能为一个函数或一个表达式,有一种情况例外。例如,一个日期列的默认值不能被设置为一个函数,如NOW()或CURRENT_DATE。不过,有一种例外,您可以对TIMESTAMP列指定CURRENT_TIMESTAMP为默认值。请参见[11.3.1.1节,“MySQL 4.1中的TIMESTAMP属性”](# "11.3.1.1. TIMESTAMP Properties as of MySQL 4.1")。 BLOB和TEXT列不能被赋予默认值。 如果在列定义中没有明确的DEFAULT值,则MySQL按照如下规则确定默认值: 如果列可以使用NULL作为值,则使用DEFAULT NULL子句对列进行定义。(在MySQL的早期版本中也如此。) 如果列不能使用NULL作为值,则MySQL对列进行定义时不使用DEFAULT子句。输入数据时,如果INSERT或REPLACE语句不包括列的值,则MySQL依据当时的有效的SQL模式操作列: o        如果严格模式没有被启用,则MySQL会根据列数据类型,把列设置为明确的默认值。 o        如果严格模式已被启用,则事务表会出现错误,语句被回滚。对于非事务表,会出现错误,不过,如果错误出现在一个多行语句中的第二行或后续行,则以前的各行将被插入。 假设表t按下面的方法进行定义: ~~~ CREATE TABLE t (i INT NOT NULL); ~~~ 在这种情况下,i没有明确的默认值,所以在严格模式中,每个后续语句都会产生一个错误,并且没有行被插入。当未使用严格模式时,只有第三个语句产生错误;明确的默认值被插入到前两个语句中,但是第三个语句会出现错误,因为DEFAULT(i)不会产生一个值: ~~~ INSERT INTO t VALUES(); ~~~ ~~~ INSERT INTO t VALUES(DEFAULT); ~~~ ~~~ INSERT INTO t VALUES(DEFAULT(i)); ~~~ 见[5.3.2节,“SQL服务器模式”](# "5.3.2. The Server SQL Mode")。 对于一个给定的表,您可以使用SHOW CREATE TABLE语句来查看那些列有明确的DEFAULT子句。 ·         对于列的评注可以使用COMMENT选项来进行指定。评注通过SHOW CREATE TABLE和SHOW FULL COLUMNS语句显示。 ·         属性SERIAL可以用作BIGINT UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE的别名。 ·         KEY通常是INDEX同义词。如果关键字属性PRIMARY KEY在列定义中已给定,则PRIMARY KEY也可以只指定为KEY。这么做的目的是与其它数据库系统兼容。 ·         在UNIQUE索引中,所有的值必须互不相同。如果您在添加新行时使用的关键字与原有行的关键字相同,则会出现错误。例外情况是,如果索引中的一个列允许包含NULL值,则此列可以包含多个NULL值。此例外情况不适用于BDB表。在BDB中,带索引的列只允许一个单一NULL。 ·         PRIMARY KEY是一个唯一KEY,此时,所有的关键字列必须定义为NOT NULL。如果这些列没有被明确地定义为NOT NULL,MySQL应隐含地定义这些列。一个表只有一个PRIMARY KEY。如果您没有PRIMARY KEY并且一个应用程序要求在表中使用PRIMARY KEY,则MySQL返回第一个UNIQUE索引,此索引没有作为PRIMARY KEY的NULL列。 ·         在已创建的表中,PRIMARY KEY的位置最靠前,然后是所有的UNIQUE索引,然后是非唯一索引。这可以帮助MySQL优化程序选择优先使用哪个索引,并且更快速的检测出重复的UNIQUE关键字。 ·         PRIMARY KEY可以是一个多列索引。但是,在列规约中使用PRIMARY KEY关键字属性无法创建多列索引。这么做只能把一个列标记为主列。您必须使用一个单独的PRIMARY KEY(index_col_name, ...)子句。 ·         如果PRIMARY KEY或UNIQUE索引只包括一个列,并且此列为整数类型,则您也可以在SELECT语句中把此列作为_rowid引用。 ·         在MySQL中,PRIMARY KEY的名称为PRIMARY。对于其它索引,如果您没有赋予名称,则索引被赋予的名称与第一个已编入索引的列的名称相同,并自选添加后缀(_2, _3,...),使名称为唯一名称。您可以使用SHOW INDEX FROM *tbl_name*来查看表的索引名称。请参见[13.5.4.11节,“SHOW INDEX语法”](# "13.5.4.11. SHOW INDEX Syntax")。 ·         部分存储引擎允许您在创建索引时指定索引类型。*index_type*指示语句的语法是USING type_name。 示例: ~~~ CREATE TABLE lookup ~~~ ~~~   (id INT, INDEX USING BTREE (id)) ~~~ ~~~   ENGINE = MEMORY; ~~~ 要了解有关USING的详细说明,请参见[13.1.4节,“CREATE INDEX语法”](# "13.1.4. CREATE INDEX Syntax")。 要了解有关MySQL如何使用索引的更多信息,请参见[7.4.5节,“MySQL如何使用索引”](# "7.4.5. How MySQL Uses Indexes")。 ·         在MySQL 5.1中,只有MyISAM,InnoDB, BDB和MEMORY存储引擎支持在含有NULL值的列中编索引。在其它情况下,您必须定义已编索引的列为NOT NULL,否则会出现错误。 ·         在一个索引规约中使用*col_name*(*length*)语法,您可以创建一个索引,此索引只使用一个CHAR或VARCHAR列的第一个*length*字符。只对列值的前缀编制索引可以使索引文件大大减小。请参见[7.4.3节,“列索引”](# "7.4.3. Column Indexes")。 MyISAM和InnoDB存储引擎也支持对BLOB和TEXT列编索引。当对BLOB或TEXT列编索引时,您必须为索引指定一个前缀长度。例如: ~~~ CREATE TABLE test (blob_col BLOB, INDEX(blob_col(10))); ~~~ 对于MyISAM和InnoDB表,前缀最长可以为1000字节,对于其它表格类型,最长可以为255字节。注意前缀长度限值以字节为单位,而在CREATE TABLE语句中的前缀长度用字符数目来表述。当为一个使用多字节字符集的列指定前缀长度时,一定要考虑到这一点。 ·         一个*index_col_name*规约可以以ASC或DESC结尾。这些关键词可以在将来进行扩展,用于指定升序或降序的索引值存储。当前,这些关键词被分析但是被忽略;索引值均以升序储存。 ·         当您在SELECT中的TEXT列或BLOB列中使用ORDER BY或GROUP BY时,服务器只使用初始的字节数目对值进行分类。字节数目由max_sort_length系统变量进行指示。请参见[11.4.3节,“BLOB和TEXT类型``”](# "11.4.3. The BLOB and TEXT Types")。 ·         您可以创建特殊的FULLTEXT索引,用于全文搜索。只有MyISAM表类型支持FULLTEXT索引。FULLTEXT索引只可以从CHAR, VARCHAR和TEXT列中创建。整个列都会被编入索引;不支持对部分列编索引。如果已指定,前缀长度会被忽略。要了解运行的详细说明,请参见[12.7节,“全文搜索功能”](# "12.7. Full-Text Search Functions")。 ·         您可以为空间列类型创建SPATIAL索引。只有MyISAM表支持空间类型,已编索引的列必须声明为NOT NULL。请参见[第19章:](#)[*MySQL中的空间扩展*](# "Chapter 19. Spatial Extensions in MySQL")。 ·         InnoDB表支持对外键限制条件进行检查。请参见[15.2节,“InnoDB存储引擎”](# "15.2. The InnoDB Storage Engine")。注意,在InnoDB中,FOREIGN KEY语法比本节开始时介绍的CREATE TABLE语句的语法更严格:被引用的表中的列必须有明确的命名。InnoDB支持外键的ON DELETE和ON UPDATE两种操作。有关精确语法的说明,请参见[15.2.6.4节,“FOREIGN KEY约束”](# "15.2.6.4. FOREIGN KEY Constraints")。 对于其它存储引擎,MySQL服务器对CREATE TABLE语句中的FOREIGN KEY和REFERENCES语法进行分析,但不采取进一步的行动。所有的存储引擎均对CHECK子句进行分析,但是忽略CHECK子句。请参见[1.8.5.5节,“外键”](# "1.8.5.5. Foreign Keys")。 ·         对于MyISAM表,每个NULL列要多占用一位,进位到距离最近的字节。最大记录长度(以字节为单位)按照如下方法计算: ~~~ ·                row length = 1 ~~~ ~~~ ·                             + (sum of column lengths) ~~~ ~~~ ·                             + (number of NULL columns + delete_flag + 7)/8 ~~~ ~~~ ·                             + (number of variable-length columns) ~~~ 对于采用静态记录格式的表,*delete_flag*为1。静态表在行记录中使用一位用作位标记。位标记指示该行是否已被删除。对于动态表,*delete_flag*为0,因为在动态行标题中已存储了位标记。 这些计算方法不适用于InnoDB表。对于InnoDB表,NULL列的存储量与NOT NULL列的存储量没有区别。 ENGINE和TYPE选项用于为表指定存储引擎。ENGINE是首选的选项名称。 ENGINE和TYPE选项采用以下值: <table border="1" cellpadding="0" id="table3"><tr><td> <p><strong><span> 存储引擎</span></strong></p></td> <td> <p><strong><span> 说明</span></strong></p></td> </tr><tr><td> <p> <span>ARCHIVE</span></p></td> <td> <p>档案存储引擎。请参见<a href="storage-engines.html#archive-storage-engine" title="15.8. The ARCHIVE Storage Engine">15.8节,“ARCHIVE存储引擎”</a>。</p></td> </tr><tr><td> <p> <span>BDB</span></p></td> <td> <p>带页面锁定的事务安全表。也称为<span>BerkeleyDB</span>。请参见<a href="storage-engines.html#bdb-storage-engine" title="15.5. The BDB (BerkeleyDB) Storage Engine">15.5节,“BDB (BerkeleyDB)存储引擎”</a>。</p></td> </tr><tr><td> <p> <span>CSV</span></p></td> <td> <p>值之间用逗号隔开的表。请参见<a href="storage-engines.html#csv-storage-engine" title="15.9. The CSV Storage Engine">15.9节,“CSV存储引擎</a>。</p></td> </tr><tr><td> <p> <span>EXAMPLE</span></p></td> <td> <p>示例引擎。请参见<a href="storage-engines.html#example-storage-engine" title="15.6. The EXAMPLE Storage Engine">15.6节,“EXAMPLE存储引擎”</a>。</p></td> </tr><tr><td> <p> <span>FEDERATED</span></p></td> <td> <p>可以访问远程表的存储引擎。请参见<a href="storage-engines.html#federated-storage-engine" title="15.7. The FEDERATED Storage Engine">15.7节,“FEDERATED存储引擎”</a>。</p></td> </tr><tr><td> <p> <span>HEAP</span></p></td> <td> <p>见<a href="storage-engines.html#memory-storage-engine" title="15.4. The MEMORY (HEAP) Storage Engine">15.4节,“MEMORY (HEAP)存储引擎”</a>。</p></td> </tr><tr><td> <p><span>(<em><span>OBSOLETE</span></em>) <span>ISAM</span></span></p></td> <td> <p>在<span>MySQL 5.1</span>中没有此引擎。如果您要从以前的版本升级到<span>MySQL 5.1</span>,您应该在进行升级前把原有的<span>ISAM</span>表转换为<span>MyISAM</span>表。请参见<a href="storage-engines.html" title="Chapter 15. Storage Engines and Table Types">第15章:<i>存储引擎和表类型</i></a>。</p></td> </tr><tr><td> <p> <span>InnoDB</span></p></td> <td> <p>带行锁定和外键的事务安全表。请参见<a href="storage-engines.html#innodb" title="15.2. The InnoDB Storage Engine">15.2节,“InnoDB存储引擎”</a>。</p></td> </tr><tr><td> <p> <span>MEMORY</span></p></td> <td> <p>本表类型的数据只保存在存储器里。(在早期<span>MySQL</span>版本中被称为<span>HEAP</span>。)</p></td> </tr><tr><td> <p> <span>MERGE</span></p></td> <td> <p><span>MyISAM</span>表的集合,作为一个表使用。也称为<span>MRG_MyISAM</span>。请参见<a href="storage-engines.html#merge-storage-engine" title="15.3. The MERGE Storage Engine">15.3节,“MERGE存储引擎”</a>。</p></td> </tr><tr><td> <p> <span>MyISAM</span></p></td> <td> <p>二进制轻便式存储引擎,此引擎是<span>MySQL</span>所用的默认存储引擎。请参见<a href="storage-engines.html#myisam-storage-engine" title="15.1. The MyISAM Storage Engine">15.1节,“MyISAM存储引擎”</a>。</p></td> </tr><tr><td> <p> <span>NDBCLUSTER</span></p></td> <td> <p>成簇表,容错表,以存储器为基础的表。也称为<span>NDB</span>。请参见<a href="ndbcluster.html">第17章:</a><a href="ndbcluster.html" title="Chapter 17. MySQL Cluster"><i>MySQL簇</i></a>。</p></td> </tr></table> 要了解有关MySQL存储引擎的更多信息,请参见[第15章:*存储引擎和表类型*](# "Chapter 15. Storage Engines and Table Types")。 如果被指定的存储引擎无法利用,则MySQL使用MyISAM代替。例如,一个表定义包括ENGINE=BDB选项,但是MySQL服务器不支持BDB表,则表被创建为MyISAM表。这样,如果您在主机上有事务表,但在从属机上创建的是非交互式表(以加快速度)时,可以进行复制设置。在MySQL 5.1中,如果没有遵守存储引擎规约,则会出现警告。 其它表选项用于优化表的性质。在多数情况下,您不必指定表选项。这些选项适用于所有存储引擎,另有说明除外: ·         AUTO_INCREMENT 表的初始AUTO_INCREMENT值。在MySQL 5.1中,本选项只适用于MyISAM和MEMORY表。InnoDB也支持本选项。如果引擎不支持AUTO_INCREMENT表选项,则要设置引擎的第一个auto-increment值,需插入一个“假”行。该行的值比创建表后的值小一,然后删除该假行。 对于在CREATE TABLE语句中支持AUTO_INCREMENT表选项的引擎,您也可以使用ALTER TABLE *tbl_name* AUTO_INCREMENT = *n*来重新设置AUTO_INCREMENT值。 ·         AVG_ROW_LENGTH 表中平均行长度的近似值。只需要对含尺寸可变的记录的大型表进行此项设置。 当创建一个MyISAM表时,MySQL使用MAX_ROWS和AVG_ROW_LENGTH选项的乘积来确定得出的表有多大。如果有一个选项未指定,则表的最大尺寸为65,536TB数据。(如果操作系统不支持这么大的文件,则表的尺寸被限定在操作系统的限值处。)如果您想缩小指针尺寸使索引更小,速度更快,并且您不需要大文件,则您可以通过设置myisam_data_pointer_size系统变量来减少默认指针的尺寸。(见[5.3.3节,“服务器系统变量”](# "5.3.3. Server System Variables")。)如果您希望所有的表可以扩大,超过默认限值,并且愿意让表稍微慢点,并稍微大点,则您可以通过设置此变量增加默认指针的尺寸。 ·         [DEFAULT] CHARACTER SET 用于为表指定一个默认字符集。CHARSET是CHARACTER SET的同义词。 对于CHARACTER SET. ·         COLLATE 用于为表指定一个默认整序。 ·         CHECKSUM 如果您希望MySQL随时对所有行进行实时检验求和(也就是,表变更后,MySQL自动更新检验求和),则应把此项设置为1。这样做,表的更新速度会略微慢些,但是更容易寻找到受损的表。CHECKSUM TABLE语句用于报告检验求和(仅限于MyISAM)。 ·         COMMENT 表的注释,最长60个字符。 ·         CONNECTION FEDERATED表的连接字符串。( 注释:较早版本的MySQL使用COMMENT选项用于连接字符串。 ·         MAX_ROWS 您打算储存在表中的行数目的最大值。这不是一个硬性限值,而更像一个指示语句,指示出表必须能存储至少这么多行。 ·         MIN_ROWS 您打算存储在表中的行数目的最小值。 ·         PACK_KEYS 如果您希望索引更小,则把此选项设置为1。这样做通常使更新速度变慢,同时阅读速度加快。把选项设置为0可以取消所有的关键字压缩。把此选项设置为DEFAULT时,存储引擎只压缩长的CHAR或VARCHAR列(仅限于MyISAM)。 如果您不使用PACK_KEYS,则默认操作是只压缩字符串,但不压缩数字。如果您使用PACK_KEYS=1,则对数字也进行压缩。 在对二进制数字关键字进行压缩时,MySQL采用前缀压缩: o        每个关键字需要一个额外的字节来指示前一个关键字中有多少字节与下一个关键字相同。 o        指向行的指针以高位字节优先的顺序存储在关键字的后面,用于改进压缩效果。 这意味着,如果两个连续行中有许多相同的关键字,则后续的“相同”的关键字通常只占用两个字节(包括指向行的指针)。与此相比,常规情况下,后续的关键字占用storage_size_for_key + pointer_size(指针尺寸通常为4)。但是,只有在许多数字相同的情况下,前缀压缩才有好处。如果所有的关键字完全不同,并且关键字不能含有NULL值,则每个关键字要多使用一个字节。(在这种情况中,储存压缩后的关键字的长度的字节与用于标记关键字是否为NULL的字节是同一字节。) ·         PASSWORD 使用密码对.frm文件加密。在标准MySQL版本中,本选项不起任何作用。 ·         DELAY_KEY_WRITE 如果您想要延迟对关键字的更新,等到表关闭后再更新,则把此项设置为1(仅限于MyISAM)。 ·         ROW_FORMAT 定义各行应如何储存。当前,此选项只适用于MyISAM表。对于静态行或长度可变行,此选项值可以为FIXED或DYNAMIC。**myisampack**用于把类型设置为COMPRESSED。请参见[15.1.3节,“MyISAM表的存储格式”](# "15.1.3. MyISAM Table Storage Formats")。 在默认情况下,InnoDB记录以压缩格式存储(ROW_FORMAT=COMPACT)。通过指定ROW_FORMAT=REDUNDANT,仍然可以申请用于较早版本的MySQL中的非压缩格式。 ·         RAID_TYPE 在MySQL 5.0中,RAID支持被删除了。要了解有关RAID的说明,请参见http://dev.mysql.com/doc/refman/4.1/en/create-table.html。 ·         UNION 当您想要把一组相同的表当作一个表使用时,采用UNION。UNION仅适用于MERGE表。请参见[15.3节,“MERGE存储引擎”](# "15.3. The MERGE Storage Engine")。 对于您映射到一个MERGE表上的表,您必须拥有SELECT, UPDATE和DELETE权限。(注释:以前,所有被使用的表必须位于同一个数据库中,并作为MERGE表。这些限制不再适用。) ·         INSERT_METHOD 如果您希望在MERGE表中插入数据,您必须用INSERT_METHOD指定应插入行的表。INSERT_METHOD选项仅用于MERGE表。使用FIRST或LAST把行插入到第一个或最后一个表中;或者使用NO,阻止插入行。请参见[15.3节,“MERGE存储引擎”](# "15.3. The MERGE Storage Engine")。 ·         DATA DIRECTORY, INDEX DIRECTORY 通过使用DATA DIRECTORY='*directory*'或INDEX DIRECTORY='*directory*',您可以指定MyISAM存储引擎放置表格数据文件和索引文件的位置。注意,目录应是通向目录的完整路径(不是相对路径)。 仅当您没有使用--skip-symbolic-links选项时,DATA DIRECTORY, INDEX DIRECTORY才能使用。操作系统必须有一个正在工作的、线程安全的realpath()调用。要了解全面信息,请参见[7.6.1.2节,“在Unix平台上使用表的符号链接](# "7.6.1.2. Using Symbolic Links for Tables on Unix")”。 ·         对于用CREATE TABLE创建的表,可以使用*partition_options*控制分区。如果使用了*partition_options*,则其中必须包含至少一个PARTITION BY子句。本子句包含用于确定分区的函数;该函数会返回一个整值,范围从1到*num*。此处*num*为分区的数目。此函数中可以使用的选项显示在下面的清单中。 要点:在本节开始时介绍的用于*partition_options*的语法中显示的选项,并不是都能用于所有分区类型。要了解各种类型具体的信息 ,请参见以下各类型的清单。要了解有关在MySQL中的分区的操作和使用情况的全面说明,以及要了解表创建的示例和与MySQL分区有关的其它命令,请参见[第18章:](#)[*分区*](# "Chapter 18. Partitioning")。 o        HASH(*expr*):用于混编一个或多个列,创建一个关键字,用于放置行,并确定行的位置。*expr*是一个表达式,使用一个或多个表中的列。该表达式可以是任何能够生成单一整值的合法的MySQL表达式(包括MySQL函数)。例如,这些都是有效的CREATE TABLE语句,语句中使用了PARTITION BY HASH: ~~~ o                     CREATE TABLE t1 (col1 INT, col2 CHAR(5)) ~~~ ~~~ o                         PARTITION BY HASH(col1); ~~~ ~~~ o                       ~~~ ~~~ o                     CREATE TABLE t1 (col1 INT, col2 CHAR(5)) ~~~ ~~~ o                         PARTITION BY HASH( ORD(col2) ); ~~~ ~~~ o                       ~~~ ~~~ o                     CREATE TABLE t1 (col1 INT, col2 CHAR(5), col3 DATETIME) ~~~ ~~~ o                         PARTITION BY HASH ( YEAR(col3) ); ~~~ VALUES LESS THAN或VALUES IN子句不能和PARTITION BY HASH一起使用。 PARTITION BY HASH使用*expr*被分区数目所除后的余数(也就是模数)。要了解示例和其它信息,请参见[18.2.3节,“HASH分区”](# "18.2.3. HASH Partitioning")。 LENEAR关键词需要一种不同的算法。在这种情况下,通过一次或多次逻辑AND运算得出的结果,计算出存储记录的分区的数目。要了解线形混编的讨论和示例,请参见[18.2.3.1节,“LINEAR HASH分区”](# "18.2.3.1. LINEAR HASH Partitioning")。 o        KEY(*column_list*):与HASH近似,除了有一点不一样,即MySQL提供了混编函数,以保证均匀的数据分布。*column_list*自变量只是各列的一个清单。本示例显示了由关键字进行分区的一个简单的表,分为4个分区: ~~~ o                     CREATE TABLE tk (col1 INT, col2 CHAR(5), col3 DATE) ~~~ ~~~ o                         PARTITION BY KEY(col3) ~~~ ~~~ o                         PARTITIONS 4; ~~~ 采用LINEAR关键词,您可以对由关键字分区的表进行线形分区。这与由HASH进行分区的表格有同样的效果;也就是说,使用&操作符查找分区数目,而不是使用模数(详细说明见[18.2.3.1节,“LINEAR HASH分区”](# "18.2.3.1. LINEAR HASH Partitioning")和[18.2.4节,“KEY分区”](# "18.2.4. KEY Partitioning"))。本示例采用了关键字线形分区,用来在5个分区之间分配数据: ~~~ CREATE TABLE tk (col1 INT, col2 CHAR(5), col3 DATE) ~~~ ~~~    PARTITION BY LINEAR KEY(col3) ~~~ ~~~     PARTITIONS 5; ~~~ VALUES LESS THAN或VALUES IN子句不能和PARTITION BY KEY一起使用。 o        RANGE:在此情况下,*expr*使用一套VALUES LESS THAN操作符显示了某一范围内的值。当使用范围分区时,您必须使用VALUES LESS THAN定义至少一个分区。VALUES IN不能和范围分区一起使用。 VALUES LESS THAN可以与一个文字值同时使用,或者与一个可以求算单一值的表达式同时使用。 举例说明,假设您有一个表,您希望采用以下方法对包含年份值的一列进行分区: <table border="1" cellpadding="0" id="table4"><tr><td> <p>分区编号:</p></td> <td> <p>年份范围:</p></td> </tr><tr><td> <p><span>0</span></p></td> <td> <p><span>1990</span>以前</p></td> </tr><tr><td> <p><span>1</span></p></td> <td> <p><span>1991 - 1994</span></p></td> </tr><tr><td> <p><span>2</span></p></td> <td> <p><span>1995 - 1998</span></p></td> </tr><tr><td> <p><span>3</span></p></td> <td> <p><span>1999 - 2002</span></p></td> </tr><tr><td> <p><span>4</span></p></td> <td> <p><span>2003 - 2005</span></p></td> </tr><tr><td> <p><span>5</span></p></td> <td> <p><span>2006</span>年以后</p></td> </tr></table> 采用这种分区方法的表可以通过如下CREATE TABLE语句实现: ~~~ CREATE TABLE t1 ( ~~~ ~~~     year_col INT, ~~~ ~~~     some_data INT ~~~ ~~~ ) ~~~ ~~~ PARTITION BY RANGE (year_col) ( ~~~ ~~~     PARTITION p0 VALUES LESS THAN (1991), ~~~ ~~~     PARTITION p1 VALUES LESS THAN (1995), ~~~ ~~~     PARTITION p2 VALUES LESS THAN (1999), ~~~ ~~~     PARTITION p3 VALUES LESS THAN (2002), ~~~ ~~~     PARTITION p4 VALUES LESS THAN (2006), ~~~ ~~~     PARTITION p5 VALUES LESS THAN MAXVALUE ~~~ ~~~ ); ~~~ PARTITION ... VALUES LESS THAN ...语句按顺序执行。VALUES LESS THAN MAXVALUE的作用是指定大于最大值的“其余”的值。 注意,VALUES LESS THAN子句按顺序执行,执行方式类似于switch ... case语段的一部分(许多编程语言,如C, Java和PHP也如此)。也就是说,子句必须按照这样一种方法排列,每一个后续的VALUES LESS THAN中指定的上限值大于前一个VALUES LESS THAN中指定的上限值,并在清单的最后加一个参照性的MAXVALUE。 VALUES IN与一系列的值同时使用。举例说明,您可以创建如下的分区方法: ~~~ CREATE TABLE client_firms ( ~~~ ~~~     id INT, ~~~ ~~~     name VARCHAR(35) ~~~ ~~~ ) ~~~ ~~~ PARTITION BY RANGE (id) ( ~~~ ~~~     PARTITION r0 VALUES IN (1, 5, 9, 13, 17, 21), ~~~ ~~~     PARTITION r1 VALUES IN (2, 6, 10, 14, 18, 22), ~~~ ~~~     PARTITION r2 VALUES IN (3, 7, 11, 15, 19, 23), ~~~ ~~~     PARTITION r3 VALUES IN (4, 8, 12, 16, 20, 24) ~~~ ~~~ ); ~~~ 当前,与VALUES IN...同时使用的值必须只包含整数值。 (因为此表只使用VALUES IN表达式进行分区,您也可以用PARTITION BY LIST代替,而不是使用PARTITION BY RANGE。请参见下一条。) 在使用VALUES LESS THAN或VALUES IN情况下,每个分区使用PARTITION *name*定义,此处*name*是分区的标识名,后面接VALUES...子句。 o        LIST(*expr*):当根据含有一系列限定性值(例如州代码或国家代码)的列进行分区时使用。在这种情况下,所有与特定的州或国家有关的记录都被分配到一个单一分区中,或者可以预留出一个分区,用于一系列特定的州或国家。LIST(*expr*)与RANGE类似,除了一点以外,即只有VALUES IN可以被用于为每个分区指定值。 当使用清单分区时,您必须使用VALUES IN定义至少一个分区。VALUES LESS THAN不能与PARTITION BY LIST一起使用。 o        分区数目可以使用PARTITION *num*子句,自选进行指定,此处,*num*是分区的数目。如果本子句和其它PARTITION子句同时使用,则*num*必须与使用PARTITION子句说明的分区的总数相等。 注释:不论您在创建一个由RANGE或LIST进行分区的表时是否使用了PARTITIONS子句,您必须在表定义中包括至少一个PARTITION VALUES(见后)。 o        一个分区可以自选分隔成多个子分区。使用自选的SUBPARTITION BY子句可以指示。子分区可以由HASH或KEY进行分隔。两种方法建立的子分区均为LINEAR。分隔子分区时的操作方式与以前描述的分区类型的操作方式一样。(无法由LIST或RANGE进行子分区分隔。) 使用SUBPARTITIONS关键词,后面接一个整值,可以对子分区的数目进行指示。 ·         使用一个*partition_definition*子句可以对每个分区分别进行定义。下面是组成这个子句的各个部分: o        PARTITION * partition_name*:用于为分区指定一个逻辑名称。 o        VALUE子句:对于范围分区,每个分区必须包括一个VALUES LESS THAN子句;对于清单分区,您必须为每个分区指定一个VALUES IN子句。本子句用于确定哪些行将被存储到此分区中。要了解语法示例,请参见[第18章:](#)[*分区*](# "Chapter 18. Partitioning")中对分区类型的讨论。 o        自选的COMMENT子句可以用于描述分区。注释必须加单引号。举例说明: ~~~ o                     COMMENT = 'Data for the years previous to 1999' ~~~ o        DATA DIRECTORY和INDEX DIRECTORY可以被用于指示本分区的数据和索引各自的存储位置的目录。*data_dir*和*index_dir*都必须是绝对系统路径。例如: ~~~ o                     CREATE TABLE th (id INT, name VARCHAR(30), adate DATE) ~~~ ~~~ o                     PARTITION BY LIST(YEAR(adate)) ~~~ ~~~ o                     ( ~~~ ~~~ o                         PARTITION p1999 VALUES IN (1995, 1999, 2003) DATA DIRECTORY = '/var/appdata/95/data' INDEX DIRECTORY = '/var/appdata/95/idx', ~~~ ~~~ o                         PARTITION p2000 VALUES IN (1996, 2000, 2004) DATA DIRECTORY = '/var/appdata/96/data' INDEX DIRECTORY = '/var/appdata/96/idx', ~~~ ~~~ o                         PARTITION p2001 VALUES IN (1997, 2001, 2005) DATA DIRECTORY = '/var/appdata/97/data' INDEX DIRECTORY = '/var/appdata/97/idx', ~~~ ~~~ o                         PARTITION p2000 VALUES IN (1998, 2002, 2006) DATA DIRECTORY = '/var/appdata/98/data' INDEX DIRECTORY = '/var/appdata/98/idx' ~~~ ~~~ ); ~~~ DATA DIRECTORY和INDEX DIRECTORY的操作方法与CREATE TABLE语句中的*table_option*子句的操作方法一样。此*table_option*子句用于位于MyISAM表管理程序下的各表。 可以为每个分区指定一个数据目录和一个索引目录。如果不指定,则数据和索引被存储在默认的MySQL数据目录中。 o        MAX_ROWS和MIN_ROWS分别用于将被存储在分区中的行数目最大值和行数目最小值。*max_number_of_rows*和*min_number_of_rows*的值必须为正整数。和具有同样名称的桌面选项一样,*max_number_of_rows*和*min_number_of_rows*只作为对服务器的“建议”值,并不是硬性限值。 o        自选的TABLESPACE子句可以用于为分区指定一个桌面空间。仅用于MySQL Cluster。 o        自选的[STORAGE] ENGINE子句可以把本分区中表的类型改为指定的类型。表的类型可以是本MySQL服务器支持的所有类型。STORAGE关键字和等号(=)均为自选项。如果没有使用此选项设置分区存储引擎,则适用于整个表的引擎可以用于此分区。 注释:分区管理程序对于PARTITION和SUBPARTITION均接受[STORAGE] ENGINE选项。目前,此子句的使用方式仅限于对所有的分区或子分区设置同一个存储引擎,如果试图在同一个表内对不同的分区或子分区设置不同的存储引擎,则会出现错误ERROR 1469 (HY000):在本版本的MySQL中,不允许在各分区中混用管理程序。我们打算在将来的MySQL 5.1版本中加入这种对分区的限定。 o        NODEGROUP选项可以用于使本分区可以作为节点组的一部分,节点组使用*node_group_id*识别。本选项仅适用于MySQL Cluster。 o        分区定义可以自选地包含一个或多个*subpartition_definition*子句。每个这种子句至少包括SUBPARTITION *name*,此处,*name*是子分区的识别名称。除了用SUBPARTITION代替PARTITION关键词外,用于子分区定义的语法与用于分区定义的语法一样。 子分区必须由HASH或KEY完成,并且只能对RANGE或LIST分区进行子分区。请参见[18.2.5节,“子分区”](# "18.2.5. Subpartitioning")。 ·         分区可以修改、合并、添加到表中,或从表中删去。要了解有关完成这些任务的MySQL命令的基本说明,请参见[13.1.2节,“ALTER TABLE语法”](# "13.1.2. ALTER TABLE Syntax")。要了解详细的说明和示例,请参见[18.3节,“分区管理”](# "18.3. Partition Management")。 您可以在CREATE TABLE语句的末尾添加一个SELECT语句,在一个表的基础上创建表。 ~~~ CREATE TABLE new_tbl SELECT * FROM orig_tbl; ~~~ MySQL会对SELECT中的所有项创建新列。举例说明: ~~~ mysql> CREATE TABLE test (a INT NOT NULL AUTO_INCREMENT, ~~~ ~~~     ->        PRIMARY KEY (a), KEY(b)) ~~~ ~~~     ->        TYPE=MyISAM SELECT b,c FROM test2; ~~~ 本语句用于创建含三个列(a, b, c)的MyISAM表。注意,用SELECT语句创建的列附在表的右侧,而不是覆盖在表上。参考以下示例: ~~~ mysql> SELECT * FROM foo; ~~~ ~~~ +---+ ~~~ ~~~ | n | ~~~ ~~~ +---+ ~~~ ~~~ | 1 | ~~~ ~~~ +---+ ~~~ ~~~   ~~~ ~~~ mysql> CREATE TABLE bar (m INT) SELECT n FROM foo; ~~~ ~~~ Query OK, 1 row affected (0.02 sec) ~~~ ~~~ Records: 1  Duplicates: 0  Warnings: 0 ~~~ ~~~   ~~~ ~~~ mysql> SELECT * FROM bar; ~~~ ~~~ +------+---+ ~~~ ~~~ | m    | n | ~~~ ~~~ +------+---+ ~~~ ~~~ | NULL | 1 | ~~~ ~~~ +------+---+ ~~~ ~~~ 1 row in set (0.00 sec) ~~~ 对应于表foo中的每一行,在表bar中插入一行,含有表foo中的值以及新列中的默认值。 在由CREATE TABLE...SELECT生成的表中,只在CREATE TABLE部分中命名的列首先出现。在两个部分中都命名的列和只在SELECT部分中命名的列随后出现。也可以通过指定CREATE TABLE部分中的列覆盖SELECT列中的数据类型。 如果在把数据复制到表中时出现错误,则表会自动被取消,不会被创建。 CREATE TABLE...SELECT不会自动创建任何索引。索引需要专门创建,以便使语句的灵活性更强。如果您希望为已创建的表建立索引,您应在SELECT语句前指定索引。 ~~~ mysql> CREATE TABLE bar (UNIQUE (n)) SELECT n FROM foo; ~~~ 列的类型会发生部分转化。例如,AUTO_INCREAMENT属性不会被保留,VARCHAR列会变成CHAR列。 当使用CREATE...SELECT创建表时,在查询时一定要对功能调用和表达式起别名。如果不起别名,则CREATE语句会出现错误或者生成不符合需要的列名称。 ~~~ CREATE TABLE artists_and_works ~~~ ~~~ SELECT artist.name, COUNT(work.artist_id) AS number_of_works ~~~ ~~~ FROM artist LEFT JOIN work ON artist.id = work.artist_id ~~~ ~~~ GROUP BY artist.id; ~~~ 您也可以明确地为一个已生成的列指定类型: ~~~ CREATE TABLE foo (a TINYINT NOT NULL) SELECT b+1 AS a FROM bar; ~~~ 根据其它表的定义(包括在原表中定义的所有的列属性和索引),使用LIKE创建一个空表: ~~~ CREATE TABLE new_tbl LIKE orig_tbl; ~~~ CREATE TABLE...LIKE不会复制对原表或外键定义指定的DATA DIRECTORY或INDEX DIRECTORY表选项。 您可以在SELECT前增加IGNORE或REPLACE,指示如何对复制唯一关键字值的记录进行操纵。使用IGNORE后,如果新记录复制了原有的唯一关键字值的记录,则新记录被丢弃。使用REPLACE后,新记录替换具有相同的唯一关键字值的记录。如果没有指定IGNORE或REPLACE,则出现多重唯一关键字值时会导致发生错误。 为了确保更新日志/二进位日志可以被用于再次创建原表,MySQL不允许在CREATE TABLE...SELECT过程中进行联合插入。 #### 13.1.5.1. 沉寂的列规格变更 在有些情况下,较早版本的MySQL会静默地更改在CREATE TABLE或ALTER TABLE语句中给定的列规约。在MySQL 5.1中不会进行这类变更。如果使用指定的数据类型无法创建列,则会出现错误。 ### 13.1.6. DROP DATABASE语法 ~~~ DROP {DATABASE | SCHEMA} [IF EXISTS] db_name ~~~ DROP DATABASE用于取消数据库中的所用表格和取消数据库。使用此语句时要非常小心!如果要使用DROP DATABASE,您需要获得数据库DROP权限。 IF EXISTS用于防止当数据库不存在时发生错误。 也可以使用DROP SCHEMA。 如果您对一个带有符号链接的数据库使用DROP DATABASE,则链接和原数据库都被取消。 DROP DATABASE会返回已被取消的表的数目。此数目相当于被取消的.frm文件的数目。 在正常操作中MySQL自身会创建出一些文件和目录。DROP DATABASE语句会从给定的数据库目录中取消这些文件和目录: ·         所有带这些扩展名的文件: <table border="1" cellpadding="0" id="table5"><tr><td> <p> <span>.BAK</span></p></td> <td> <p> <span>.DAT</span></p></td> <td> <p> <span>.HSH</span></p></td> <td> <p><span> </span></p></td> </tr><tr><td> <p> <span>.MRG</span></p></td> <td> <p> <span>.MYD</span></p></td> <td> <p> <span>.ISD</span></p></td> <td> <p><span> </span></p></td> </tr><tr><td> <p> <span>.MYI</span></p></td> <td> <p> <span>.db</span></p></td> <td> <p> <span>.frm</span></p></td> <td> <p><span> </span></p></td> </tr></table> ·         名称中包含两位16进制数00-ff的所有子目录。这些子目录用于RAID表。(当对RAID表的支持被取消时,在MySQL 5.0中,这些目录不会被取消。您应该在升级到MySQL 5.0或更新的版本前转化原有的RAID表,并人工取消这些目录。请参见MySQL 5.0参考手册中有关从较早版本升级到MySQL 5.0的章节。MySQL 5.0参考手册可以从MySQL网站中获取。) ·         db.opt文件 如果在MySQL取消了上述这些文件之后,在数据库目录中仍保留有其它文件和目录,则数据库目录不能被取消。在这种情况下,您必须人工取消所有保留下的文件或目录,并再次发送DROP DATABASE语句。 您还可以使用**mysqladmin**来取消文件。请参见[8.5节,“mysqladmin:用于管理MySQL服务器的客户端”](# "8.5. mysqladmin — Client for Administering a MySQL Server")。 ### 13.1.7. DROP INDEX语法 ~~~ DROP INDEX index_name ON tbl_name ~~~ DROP INDEX用于从表*tbl_name*中取消名称为*index_name*的索引。本语句被映射到一个ALTER TABLE语句中,用于取消索引。请参见[13.1.2节,“ALTER TABLE语法”](# "13.1.2. ALTER TABLE Syntax")。 ### 13.1.8. DROP TABLE语法 ~~~ DROP [TEMPORARY] TABLE [IF EXISTS] ~~~ ~~~     tbl_name [, tbl_name] ... ~~~ ~~~     [RESTRICT | CASCADE] ~~~ DROP TABLE用于取消一个或多个表。您必须有每个表的DROP权限。所有的表数据和表定义会被取消,所以使用本语句要小心! 注意,对于一个带分区的表,DROP TABLE会永久性地取消表定义,取消各分区,并取消储存在这些分区中的所有数据。DROP TABLE还会取消与被取消的表有关联的分区定义(.par)文件。 对与不存在的表,使用IF EXISTS用于防止错误发生。当使用IF EXISTS时,对于每个不存在的表,会生成一个NOTE。请参见[13.5.4.22节,“SHOW WARNINGS语法”](# "13.5.4.22. SHOW WARNINGS Syntax")。 RESTRICT和CASCADE可以使分区更容易。目前,RESTRICT和CASCADE不起作用。 注释:除非您使用TEMPORARY关键词,DROP TABLE会自动提交当前的有效的事务。 TEMPORARY关键词具有以下作用: ·         语句只取消TEMPORARY表。 ·         语句不会终止正在进行中的事务。 ·         不会查验存取权。(TEMPORARY表仅对于创建该表的客户端是可见的,所以查验是不必要的。) 使用TEMPORARY是确保您不会意外取消一个非TEMPORARY表的良好方法。 ### 13.1.9. RENAME TABLE语法 ~~~ RENAME TABLE tbl_name TO new_tbl_name ~~~ ~~~     [, tbl_name2 TO new_tbl_name2] ... ~~~ 本语句用于对一个或多个表进行重命名。 重命名操作自动进行,这意味着当重命名正在运行时,其它线程不能读取任何表。例如,如果您有一个原有的表old_table,您可以创建另一个具有相同结构的空表new_table,然后用此空表替换原有的表: ~~~ CREATE TABLE new_table (...); ~~~ ~~~ RENAME TABLE old_table TO backup_table, new_table TO old_table; ~~~ 如果此语句用于对多个表进行重命名,则重命名操作从左至右进行。如果您想要交换两个表的名称,您可以这样做(假设不存在名称为tmp_table的表): ~~~ RENAME TABLE old_table TO tmp_table, ~~~ ~~~              new_table TO old_table, ~~~ ~~~              tmp_table TO new_table; ~~~ 只要两个数据库位于同一文件系统中,您还可以对表进行重命名,把表从一个数据库中移动到另一个数据库中: ~~~ RENAME TABLE current_db.tbl_name TO other_db.tbl_name; ~~~ 当您执行RENAME时,您不能有被锁定的表,也不能有处于活性状态的事务。您还必须拥有原表的ALTER和DROP权限,以及新表的CREATE和INSERT权限。 如果MySQL对多个表进行重命名时遇到了错误,MySQL会对所有已被重命名的表进行反向重命名,返回到原来的状态。 只要您不尝试通过重命名把视图加入另一个数据库中,则RENAME TABLE也可以用于视图。 ### 13.2. 数据操作语句 [13.2.1. DELETE语法](#)[13.2.2. DO语法](#)[13.2.3. HANDLER语法](#)[13.2.4. INSERT语法](#)[13.2.5. LOAD DATA INFILE语法](#)[13.2.6. REPLACE语法](#)[13.2.7. SELECT语法](#)[ 13.2.8. Subquery语法](#)[13.2.9. TRUNCATE语法](#)[13.2.10. UPDATE语法](#) ### 13.2.1. DELETE语法 单表语法: ~~~ DELETE [LOW_PRIORITY] [QUICK] [IGNORE] FROM tbl_name ~~~ ~~~     [WHERE where_definition] ~~~ ~~~     [ORDER BY ...] ~~~ ~~~     [LIMIT row_count] ~~~ 多表语法: ~~~ DELETE [LOW_PRIORITY] [QUICK] [IGNORE] ~~~ ~~~     tbl_name[.*] [, tbl_name[.*] ...] ~~~ ~~~     FROM table_references ~~~ ~~~     [WHERE where_definition] ~~~ 或: ~~~ DELETE [LOW_PRIORITY] [QUICK] [IGNORE] ~~~ ~~~     FROM tbl_name[.*] [, tbl_name[.*] ...] ~~~ ~~~     USING table_references ~~~ ~~~     [WHERE where_definition] ~~~ *tbl_name*中有些行满足由*where_definition*给定的条件。DELETE用于删除这些行,并返回被删除的记录的数目。 如果您编写的DELETE语句中没有WHERE子句,则所有的行都被删除。当您不想知道被删除的行的数目时,有一个更快的方法,即使用TRUNCATE TABLE。请参见[13.2.9节,“TRUNCATE语法”](# "13.2.9. TRUNCATE Syntax")。 如果您删除的行中包括用于AUTO_INCREMENT列的最大值,则该值被重新用于BDB表,但是不会被用于MyISAM表或InnoDB表。如果您在AUTOCOMMIT模式下使用DELETE FROM *tbl_name*(不含WHERE子句)删除表中的所有行,则对于所有的表类型(除InnoDB和MyISAM外),序列重新编排。对于InnoDB表,此项操作有一些例外,在[15.2.6.3节,“AUTO_INCREMENT列如何在InnoDB中运行”](# "15.2.6.3. How an AUTO_INCREMENT Column Works in InnoDB")中进行了讨论。 对于MyISAM和BDB表,您可以把AUTO_INCREMENT次级列指定到一个多列关键字中。在这种情况下,从序列的顶端被删除的值被再次使用,甚至对于MyISAM表也如此。请参见[3.6.9节,“使用AUTO_INCREMENT”](# "3.6.9. Using AUTO_INCREMENT")。 DELETE语句支持以下修饰符: ·         如果您指定LOW_PRIORITY,则DELETE的执行被延迟,直到没有其它客户端读取本表时再执行。 ·         对于MyISAM表,如果您使用QUICK关键词,则在删除过程中,存储引擎不会合并索引端结点,这样可以加快部分种类的删除操作的速度。 ·         在删除行的过程中,IGNORE关键词会使MySQL忽略所有的错误。(在分析阶段遇到的错误会以常规方式处理。)由于使用本选项而被忽略的错误会作为警告返回。 删除操作的速度会受到一些因素的影响,这些因素在[7.2.18节,“DELETE语句的速度”](# "7.2.18. Speed of DELETE Statements")中进行了讨论。 在MyISAM表中,被删除的记录被保留在一个带链接的清单中,后续的INSERT操作会重新使用旧的记录位置。要重新使用未使用的空间并减小文件的尺寸,则使用OPTIMIZE TABLE语句或**myisamchk**应用程序重新编排表。OPTIMIZE TABLE更简便,但是**myisamchk**速度更快。请参见[13.5.2.5节,“OPTIMIZE TABLE语法”](# "13.5.2.5. OPTIMIZE TABLE Syntax")和[第7章:](#)[*优化*](# "Chapter 7. Optimization")。 QUICK修饰符会影响到在删除操作中索引端结点是否合并。当用于被删除的行的索引值被来自后插入的行的相近的索引值代替时,DELETE QUICK最为适用。在此情况下,被删除的值留下来的空穴被重新使用。 未充满的索引块跨越某一个范围的索引值,会再次发生新的插入。当被删除的值导致出现未充满的索引块时,DELETE QUICK没有作用。在此情况下,使用QUICK会导致未利用的索引中出现废弃空间。下面是此种情况的举例说明: 1.    创建一个表,表中包含已编索引的AUTO_INCREMENT列。 2.    在表中插入很多记录。每次插入会产生一个索引值,此索引值被添加到索引的高端处。 3.    使用DELETE QUICK从列的低端处删除一组记录。 在此情况下,与被删除的索引值相关的索引块变成未充满的状态,但是,由于使用了QUICK,这些索引块不会与其它索引块合并。当插入新值时,这些索引块仍为未充满的状态,原因是新记录不含有在被删除的范围内的索引值。另外,即使您此后使用DELETE时不包含QUICK,这些索引块也仍是未充满的,除非被删除的索引值中有一部分碰巧位于这些未充满的块的之中,或与这些块相邻。在这些情况下,如果要重新利用未使用的索引空间,需使用OPTIMIZE TABLE。 如果您打算从一个表中删除许多行,使用DELETE QUICK再加上OPTIMIZE TABLE可以加快速度。这样做可以重新建立索引,而不是进行大量的索引块合并操作。 用于DELETE的MySQL唯一的LIMIT*row_count*选项用于告知服务器在控制命令被返回到客户端前被删除的行的最大值。本选项用于确保一个DELETE语句不会占用过多的时间。您可以只重复DELETE语句,直到相关行的数目少于LIMIT值为止。 如果DELETE语句包括一个ORDER BY子句,则各行按照子句中指定的顺序进行删除。此子句只在与LIMIT联用是才起作用。例如,以下子句用于查找与WHERE子句对应的行,使用timestamp_column进行分类,并删除第一(最旧的)行: ~~~ DELETE FROM somelog ~~~ ~~~ WHERE user = 'jcole' ~~~ ~~~ ORDER BY timestamp_column ~~~ ~~~ LIMIT 1; ~~~ 您可以在一个DELETE语句中指定多个表,根据多个表中的特定条件,从一个表或多个表中删除行。不过,您不能在一个多表DELETE语句中使用ORDER BY或LIMIT。 *table_references*部分列出了包含在联合中的表。此语法在[13.2.7.1节,“JOIN语法”](# "13.2.7.1. JOIN Syntax")中进行了说明。 对于第一个语法,只删除列于FROM子句之前的表中的对应的行。对于第二个语法,只删除列于FROM子句之中(在USING子句之前)的表中的对应的行。作用是,您可以同时删除许多个表中的行,并使用其它的表进行搜索: ~~~ DELETE t1, t2 FROM t1, t2, t3 WHERE t1.id=t2.id AND t2.id=t3.id; ~~~ 或: ~~~ DELETE FROM t1, t2 USING t1, t2, t3 WHERE t1.id=t2.id AND t2.id=t3.id; ~~~ 当搜索待删除的行时,这些语句使用所有三个表,但是只从表t1和表t2中删除对应的行。 以上例子显示了使用逗号操作符的内部联合,但是多表DELETE语句可以使用SELECT语句中允许的所有类型的联合,比如LEFT JOIN。 本语法允许在名称后面加.*,以便与Access相容。 如果您使用的多表DELETE语句包括InnoDB表,并且这些表受外键的限制,则MySQL优化程序会对表进行处理,改变原来的从属关系。在这种情况下,该语句出现错误并返回到前面的步骤。要避免此错误,您应该从单一表中删除,并依靠InnoDB提供的ON DELETE功能,对其它表进行相应的修改。 注释:当引用表名称时,您必须使用别名(如果已给定): ~~~ DELETE t1 FROM test AS t1, test2 WHERE ... ~~~ 进行多表删除时支持跨数据库删除,但是在此情况下,您在引用表时不能使用别名。举例说明: ~~~ DELETE test1.tmp1, test2.tmp2 FROM test1.tmp1, test2.tmp2 WHERE ... ~~~ 目前,您不能从一个表中删除,同时又在子查询中从同一个表中选择。 ### 13.2.2. DO语法 ~~~ DO expr [, expr] ... ~~~ DO用于执行表达式,但是不返回任何结果。DO是SELECT*expr*的简化表达方式。DO有一个优势,就是如果您不太关心结果的话,DO的速度稍快。 DO主要用于执行有副作用的函数,比如RELEASE_LOCK()。 ### 13.2.3. HANDLER语法 ~~~ HANDLER tbl_name OPEN [ AS alias ] HANDLER tbl_name READ index_name { = | >= | <= | < } (value1,value2,...) [ WHERE where_condition ] [LIMIT ... ] HANDLER tbl_name READ index_name { FIRST | NEXT | PREV | LAST } [ WHERE where_condition ] [LIMIT ... ] HANDLER tbl_name READ { FIRST | NEXT } [ WHERE where_condition ] [LIMIT ... ] HANDLER tbl_name CLOSE ~~~ HANDLER语句提供通往表存储引擎接口的直接通道。HANDLER可以用于MyISAM和InnoDB表。 HANDLER...OPEN语句用于打开一个表,通过后续的HANDLER...READ语句建立读取表的通道。本表目标不会被其它线程共享,也不会关闭,直到线程调用HANDLER...CLOSE或线程中止时为止。如果您使用一个别名打开表,则使用其它HANDLER语句进一步参阅表是必须使用此别名,而不能使用表名。 如果被指定的索引满足给定的值并且符合了WHERE条件,则第一个HANDLER...READ语法取出一行。如果您有一个多列索引,则指定索引列值为一个用逗号隔开的清单。既可以为索引中的所有列指定值,也可以为索引列的最左边的前缀指定值。假设一个索引包括三个列,名称为col_a, col_b,和col_c,并按此顺序排列。HANDLER语句可以为索引中的所有三个列指定值,或者为一个最左边前缀中的各列指定值。举例说明: ~~~ HANDLER ... index_name = (col_a_val,col_b_val,col_c_val) ... ~~~ ~~~ HANDLER ... index_name = (col_a_val,col_b_val) ... ~~~ ~~~ HANDLER ... index_name = (col_a_val) ... ~~~ 第二个HANDLER...READ语法按索引的顺序从表中取出一行。索引的顺序符合WHERE条件。 第三个HANDLER...READ语法按自然行的顺序从表中取出一行。自然行的顺序符合WHERE条件。当想要对整个表进行扫描时,此语句比HANDLER *tbl_name* READ index_name更快。自然行的顺序指的是行存储在MyISAM表数据文件的顺序。本语句也适用于InnoDB表,但是因为没有独立的数据文件,所以没有这类概念。 不使用LIMIT子句时,所有形式的HANDLER...READ语句均只取出一行。 如果要返回多个行,应加入一个LIMIT子句。本语句于SELECT语句的语法一样。请参见[13.2.7节,“SELECT语法”](# "13.2.7. SELECT Syntax")。 HANDLER...CLOSE用于关闭使用HANDLER...OPEN打开的表。 注释:要使用HANDLER接口来查阅一个表的PRIMARY KEY,应使用带引号的识别符`PRIMARY`: ~~~ HANDLER tbl_name READ `PRIMARY` > (...); ~~~ HANDLER是比较低级别的语句。例如,它不能提供一致性。也就是说,HANDLER...OPEN不能为表做快照,也不能锁定表。这意味着,当一个HANDLER...OPEN语句被编写后,表数据可以被更改(用此线程或用其它线程),并且这些更改只会部分地出现在HANDLER...NEXT或HANDLER...PREV扫描中。 使用HANDLER接口代替常规的SELECT语句有多个原因: ·         HANDLER比SELECT更快: o        一个指定的存储引擎管理程序目标为了HANDLER...OPEN进行整序。该目标被重新用于该表的后续的HANDLER语句;不需要对每个语句进行重新初始化。 o        涉及的分析较少。 o        没有优化程序或查询校验开销。 o        在两个管理程序请求之间,不需要锁定表。 o        管理程序接口不需要提供外观一致的数据(例如,允许无条理的读取),所以存储引擎可以使用优化,而SELECT通常不允许使用优化。 ·         有些应用程序使用与ISAM近似的接口与MySQL连接。使用HANDLER可以更容易地与这些应用程序连接。 ·         HANDLER允许您采用一种特殊的方式进出数据库。而使用SELECT时难以采用(或不可能采用)这种方式。有些应用程序可以提供一个交互式的用户接口与数据库连接。当与这些应用程序同时使用时,用HANDLER接口观看数据更加自然。 ### 13.2.4. INSERT语法 [13.2.4.1. INSERT ... SELECT语法](#)[13.2.4.2. INSERT DELAYED语法](#) ~~~ INSERT [LOW_PRIORITY | DELAYED | HIGH_PRIORITY] [IGNORE] ~~~ ~~~     [INTO] tbl_name [(col_name,...)] ~~~ ~~~     VALUES ({expr | DEFAULT},...),(...),... ~~~ ~~~     [ ON DUPLICATE KEY UPDATE col_name=expr, ... ] ~~~ 或: ~~~ INSERT [LOW_PRIORITY | DELAYED | HIGH_PRIORITY] [IGNORE] ~~~ ~~~     [INTO] tbl_name ~~~ ~~~     SET col_name={expr | DEFAULT}, ... ~~~ ~~~     [ ON DUPLICATE KEY UPDATE col_name=expr, ... ] ~~~ 或: ~~~ INSERT [LOW_PRIORITY | HIGH_PRIORITY] [IGNORE] ~~~ ~~~     [INTO] tbl_name [(col_name,...)] ~~~ ~~~     SELECT ... ~~~ ~~~     [ ON DUPLICATE KEY UPDATE col_name=expr, ... ] ~~~ INSERT用于向一个已有的表中插入新行。INSERT...VALUES和INSERT...SET形式的语句根据明确指定的值插入行。INSERT...SELECT形式的语句插入从其它表中选出的行。在[13.2.4.1节,“INSERT ... SELECT语法”](# "13.2.4.1. INSERT ... SELECT Syntax")中对INSERT...SELECT进行了进一步的讨论。 行应被插入到*tbl_name*表中。可以按以下方法指定列。本语句向这些列提供值。 ·         列名称清单或SET子句明确的指示了列。 ·         如果您不为INSERT...VALUES或INSERT...SELECT指定列的清单,则表中每列的值必须在VALUES清单中提供,或由SELECT提供。如果您不知道表中各列的顺序,则使用DESCRIBE *tbl_name*查询。 列值可以采用多种方法给定: ·         如果不是在严格模式下运行,则所有没有明确给定值的列都被设置为默认值(明确的或隐含的)。例如,如果您指定了一个列清单,但此清单没有对表中所有的列进行命名,则未命名的各列被设置为默认值。默认值的赋值在[13.1.5节,“CREATE TABLE语法”](# "13.1.5. CREATE TABLE Syntax")中进行了说明。也可参见[1.8.6.2节,“对无效数据的约束”](# "1.8.6.2. Constraints on Invalid Data")。 有时候,您需要对所有没有默认值的列明确地指定值。如果您希望,在没有明确指定值时,INSERT语句可以生成错误信息,则您应该使用STRICT模式。请参见[5.3.2节,“SQL服务器模式”](# "5.3.2. The Server SQL Mode")。 ·         使用关键词DEFAULT,明确地把列设置为默认值。这样,编写向所有列赋值的INSERT语句时可以更容易,因为使用DEFAULT可以避免编写出不完整的、未包含全部列值的VALUES清单。如果不使用DEFUALT,您必须编写一个列名称清单,与VALUES清单中的每个值对应。 您还可以使用DEFAULT(*col_name*)作为一种更通用的形式,在表达式中使用,用于生成一个列的默认值。 ·         如果列清单和VALUES清单均为空清单,则INSERT会创建一个行,每个列都被设置为默认值: ~~~ ·                mysql> INSERT INTO tbl_name () VALUES(); ~~~ 在STRICT模式中,如果有一列没有默认值,则会出现错误。或者,MySQL会对所有没有明确定义默认值的列使用隐含的默认值。 ·         您可以指定一个表达式*expr*来提供一个列值。如果表达式的类型与列值不匹配,这样做会造成类型转化。并且,给定值的转化会导致不同的插入值,插入何值由列类型而定。例如,向一个INT, FLOAT, DECIMAL(10,6)或YEAR列插入字符串'1999.0e-2',插入值分别是1999,19.9921,19.992100和1999。存储在INT和YEAR列中的值为1999的原因是,在从字符串到整数的转化中,只把字符串的前面部分看作有效的整数或年份。对于浮点列和固定点列,在从字符串到浮点的转化中,把整个字符串均看作有效的浮点值。 表达式*expr*可以引用在值清单中已设置的所有列。例如,您可以这么操作,因为用于col2的值引用了col1,而col1已经被赋值: ~~~ mysql> INSERT INTO tbl_name (col1,col2) VALUES(15,col1*2); ~~~ 但是以下语句不合法,因为用于col1的值引用了col2,而col2在col1之后被赋值: ~~~ mysql> INSERT INTO tbl_name (col1,col2) VALUES(col2*2,15); ~~~ 有一种例外情况,那就是含有AUTO_INCREMENT值的列。因为AUTO_INCREMENT值在其它值赋值之后被生成,所以任何在赋值时对AUTO_INCREMENT列的引用都会返回0。 INSERT语句支持下列修改符: ·         如果您使用DELAYED关键字,则服务器会把待插入的行放到一个缓冲器中,而发送INSERT DELAYED语句的客户端会继续运行。如果表正在被使用,则服务器会保留这些行。当表空闲时,服务器开始插入行,并定期检查是否有新的读取请求。如果有新的读取请求,则被延迟的行被延缓执行,直到表再次空闲时为止。请参见[13.2.4.2节,“INSERT DELAYED语法”](# "13.2.4.2. INSERT DELAYED Syntax")。 ·         如果您使用LOW_PRIORITY关键词,则INSERT的执行被延迟,直到没有其它客户端从表中读取为止。当原有客户端正在读取时,有些客户端刚开始读取。这些客户端也被包括在内。此时,INSERT LOW_PRIORITY语句等候。因此,在读取量很大的情况下,发出INSERT LOW_PRIORITY语句的客户端有可能需要等待很长一段时间(甚至是永远等待下去)。(这与INSERT DELAYED形成对比,INSERT DELAYED立刻让客户端继续执行。请参见[13.2.4.2节,“INSERT DELAYED语法”](# "13.2.4.2. INSERT DELAYED Syntax")。)注意LOW_PRIORITY通常不应用于MyISAM表,因为这么做会取消同时进行的插入。请参见[15.1节,“MyISAM存储引擎”](# "15.1. The MyISAM Storage Engine")。 ·         如果您指定了HIGH_PRIORITY,同时服务器采用--low-priority-updates选项启动,则HIGH_PRIORITY将覆盖--low-priority-updates选项。这么做还会导致同时进行的插入被取消。 ·         使用mysql_affected_rows() C API函数,可以获得用于INSERT的受影响行的值。请参见[25.2.3.1节,“mysql_affected_rows()”](# "25.2.3.1. mysql_affected_rows()")。 ·         如果您在一个INSERT语句中使用IGNORE关键词,在执行语句时出现的错误被当作警告处理。例如,没有使用IGNORE时,如果一个行复制了原有的UNIQUE索引或PRIMARY KEY值,会导致出现重复关键字错误,语句执行失败。使用IGNORE时,该行仍然未被插入,但是不会出现错误。IGNORE未被指定时,如果数据转化引发错误,则会使语句执行失败。使用IGNORE后,无效数据被调整到最接近的值,并被插入;此时,生成警告,但是语句执行不会失败。您可以使用mysql_info() C API函数测定有多少行被插入到表中。 如果您指定了ON DUPLICATE KEY UPDATE,并且插入行后会导致在一个UNIQUE索引或PRIMARY KEY中出现重复值,则执行旧行UPDATE。例如,如果列a被定义为UNIQUE,并且包含值1,则以下两个语句具有相同的效果: ~~~ mysql> INSERT INTO table (a,b,c) VALUES (1,2,3) ~~~ ~~~     -> ON DUPLICATE KEY UPDATE c=c+1; ~~~ ~~~   ~~~ ~~~ mysql> UPDATE table SET c=c+1 WHERE a=1; ~~~ 如果行作为新记录被插入,则受影响行的值为1;如果原有的记录被更新,则受影响行的值为2。 注释:如果列b也是唯一列,则INSERT与此UPDATE语句相当: ~~~ mysql> UPDATE table SET c=c+1 WHERE a=1 OR b=2 LIMIT 1; ~~~ 如果a=1 OR b=2与多个行向匹配,则只有一个行被更新。通常,您应该尽量避免对带有多个唯一关键字的表使用ON DUPLICATE KEY子句。 您可以在UPDATE子句中使用VALUES(col_name)函数从INSERT...UPDATE语句的INSERT部分引用列值。换句话说,如果没有发生重复关键字冲突,则UPDATE子句中的VALUES(*col_name*)可以引用被插入的*col_name*的值。本函数特别适用于多行插入。VALUES()函数只在INSERT...UPDATE语句中有意义,其它时候会返回NULL。 示例: ~~~ mysql> INSERT INTO table (a,b,c) VALUES (1,2,3),(4,5,6) ~~~ ~~~     -> ON DUPLICATE KEY UPDATE c=VALUES(a)+VALUES(b); ~~~ 本语句与以下两个语句作用相同: ~~~ mysql> INSERT INTO table (a,b,c) VALUES (1,2,3) ~~~ ~~~     -> ON DUPLICATE KEY UPDATE c=3; ~~~ ~~~ mysql> INSERT INTO table (a,b,c) VALUES (4,5,6) ~~~ ~~~     -> ON DUPLICATE KEY UPDATE c=9; ~~~ 当您使用ON DUPLICATE KEY UPDATE时,DELAYED选项被忽略。 您可以使用SQL LAST_INSERT_ID()函数查找用于AUTO_INCREMENT列的值。从C API的内部,使用mysql_insert_id()函数。不过,您应该注意,两个函数的作用并不总是相同的。在[12.9.3节,“信息函数”](# "12.9.3. Information Functions")和[25.2.3.36节,“mysql_insert_id()”](# "25.2.3.36. mysql_insert_id()")中进一步讨论了与AUTO_INCREMENT列有关的INSERT语句的作用。 如果您使用INSERT...VALUES语句时采用了多个值清单或INSERT...SELECT,则该语句按以下格式返回一个信息字符串: ~~~ Records: 100 Duplicates: 0 Warnings: 0 ~~~ 记录指示了经过语句处理的行的数目。(因为重复数目可以不是零,所以该数目不一定是实际被插入的行的数目。)重复数目指的是不能被插入的行的数目,因为这些行会复制部分原有的唯一索引值。警告指的是插入有错误或有问题的列值的次数。在以下情况下会出现警告: ·         向一个已定义为NOT NULL的列中插入NULL。对于一个多行INSERT语句或INSERT INTO...SELECT语句,根据列数据的类型,列被设置为隐含的默认值。对于数字类型,默认值为0;对于字符串类型,默认值为空字符串('');对于日期和时间类型,默认值为“zero”值。对INSERT INTO...SELECT语句的处理方法与对多行插入的处理方法一样,因为服务器不能检测来自SELECT的结果,不能判断是否返回单一行。(对于单一行INSERT,当NULL被插入一个NOT NULL列时,不会出现警告,而是出现错误,并且语句运行失败。) ·         数字列的值被设置在列的值范围之外。此值被修改为未最接近的值范围端点。 ·         向一个数字列赋予一个例如'10.34 a'的值。尾部的非数字文本被删节,其余的数字部分被插入,如果字符串值没有前导的数字部分,则该列被设置为0。 ·         向一个字符串列(CHAR, VARCHAR, TEXT或BLOB)中插入的字符串超过了列的最大长度。此值被删节到列的最大长度。 ·         向日期或时间列中插入的值对于该列的类型是不合法的。根据列的类型,该列被设置到相应的零值。 如果您正在使用C API,则可以通过调用mysql_info()函数获取信息字符串。请参见[25.2.3.34节,“mysql_info()”](# "25.2.3.34. mysql_info()")。 #### 13.2.4.1. INSERT ... SELECT语法 ~~~ INSERT [LOW_PRIORITY | HIGH_PRIORITY] [IGNORE] ~~~ ~~~     [INTO] tbl_name [(col_name,...)] ~~~ ~~~     SELECT ... ~~~ ~~~     [ ON DUPLICATE KEY UPDATE col_name=expr, ... ] ~~~ 使用INSERT...SELECT,您可以快速地从一个或多个表中向一个表中插入多个行。 示例: ~~~ INSERT INTO tbl_temp2 (fld_id) ~~~ ~~~     SELECT tbl_temp1.fld_order_id ~~~ ~~~     FROM tbl_temp1 WHERE tbl_temp1.fld_order_id > 100; ~~~ 使用INSERT...SELECT语句时会出现以下情况: ·         明确地指定IGNORE,用于忽略会导致重复关键字错误的记录。 ·         不要同时使用DELAYED和INSERT...SELECT。 ·         INSERT语句的目标表会显示在查询的SELECT部分的FROM子句中。(在有些旧版本的MySQL中不会出现这种情况。) ·         AUTO_INCREMENT列照常运行。 ·         为了确保二进制日志可以被用于再次创建原表,MySQL不允许在INSERT...SELECT运行期间同时进行插入操作。 ·         目前,您不能在向一个表插入的同时,又在一个子查询中从同一个表中选择。 在ON DUPLICATE KEY UPDATE的值部分中,只要您不使用SELECT部分中的GROUP BY,您就可以引用在其它表中的列。有一个副作用是,您必须使值部分中的非唯一列的名称符合要求。 您可以使用REPLACE替代INSERT,来覆盖旧行。对于包含唯一关键字值,并复制了旧行的新行,在进行处理时,REPLACE可以作为INSERT IGNORE的同类子句:新行被用于替换旧行,而不是被丢弃。 #### 13.2.4.2. INSERT DELAYED语法 ~~~ INSERT DELAYED ... ~~~ 用于INSERT语句的DELAYED选项是MySQL相对于标准SQL的扩展。如果您的客户端不能等待INSERT完成,则这个选项是非常有用的。当您使用MySQL进行日志编写时,这是非常常见的问题。您也可以定期运行SELECT和UPDATE语句,这些语句花费的时间较长。 当一个客户端使用INSERT DELAYED时,会立刻从服务器处得到一个确定。并且行被排入队列,当表没有被其它线程使用时,此行被插入。 使用INSERT DELAYED的另一个重要的好处是,来自许多客户端的插入被集中在一起,并被编写入一个块。这比执行许多独立的插入要快很多。 使用DELAYED时有一些限制: ·         INSERT DELAYED仅适用于MyISAM, MEMORY和ARCHIVE表。对于MyISAM表,如果在数据文件的中间没有空闲的块,则支持同时采用SELECT和INSERT语句。在这些情况下,基本不需要对MyISAM使用INSERT DELAYED。请参见[15.1节,“MyISAM存储引擎”](# "15.1. The MyISAM Storage Engine"), [ 15.4节,“MEMORY (HEAP)存储引擎”](# "15.4. The MEMORY (HEAP) Storage Engine")和[15.8节,“ARCHIVE存储引擎”](# "15.8. The ARCHIVE Storage Engine")。 ·         INSERT DELAYED应该仅用于指定值清单的INSERT语句。服务器忽略用于INSERT DELAYED...SELECT语句的DELAYED。 ·         服务器忽略用于INSERT DELAYED...ON DUPLICATE UPDATE语句的DELAYED。 ·         因为在行被插入前,语句立刻返回,所以您不能使用LAST_INSERT_ID()来获取AUTO_INCREMENT值。AUTO_INCREMENT值可能由语句生成。 ·         对于SELECT语句,DELAYED行不可见,直到这些行确实被插入了为止。 ·         DELAYED在从属复制服务器中被忽略了,因为DELAYED不会在从属服务器中产生与主服务器不一样的数据。 注意,目前在队列中的各行只保存在存储器中,直到它们被插入到表中为止。这意味着,如果您强行中止了**mysqld**(例如,使用kill -9)或者如果**mysqld**意外停止,则所有没有被写入磁盘的行都会丢失。 以下详细描述了当您对INSERT或REPLACE使用DELAYED选项时会发生什么情况。在这些描述中,“线程”指的是已接受了一个INSERT DELAYED语句的线程,“管理程序”指的是为某个特定的表处理所有INSERT DELAYED语句的线程。 ·         当一个线程对一个表执行DELAYED语句时,会创建出一个管理程序线程(如果原来不存在),对用于本表的所有DELAYED语句进行处理。 ·         线程会检查是否管理程序以前已获取了DELAYED锁定;如果没有获取,则告知管理程序线程进行此项操作。即使其它线程对表有READ或WRITE锁定,也可以获得DELAYED锁定。但是管理程序会等待所有的ALTER TABLE锁定或FLUSH TABLE锁定,以确保表的结构是最新的。 ·         线程执行INSERT语句,但不是把行写入表中,而是把最终行的拷贝放入一个由管理程序线程管理的队列中。线程会提示出现语法错误,这些错误会被报告到客户端中。 ·         因为在插入操作之前,INSERT返回已经完成,所以客户端不能从服务器处获取重复记录的数目,也不能获取生成的行的AUTO_INCREMENT值。(如果您使用C API,则出于同样的原因,mysql_info()函数不会返回任何有意义的东西。) ·         当行被插入表中时,二进制日志被管理程序线程更新。在多行插入情况下,当第一行被插入时,二进制日志被更新。 ·         每次delayed_insert_limit行被编写时,管理程序会检查是否有SELECT语句仍然未执行。如果有,则会在继续运行前,让这些语句先执行。 ·         当管理程序的队列中没有多余的行时,表被解锁。如果在delayed_insert_timeout时间内,没有接收到新的INSERT DELAYED语句,则管理程序中止。 ·         如果在某个特定的管理程序队列中,有超过delayed_queue_size的行未被执行,则申请INSERT DELAYED的线程会等待,直到队列中出现空间为止。这么做可以确保**mysqld**不会把所有的存储器都用于被延迟的存储队列。 ·         管理程序线程会显示在MySQL进程清单中,其命令列中包含delayed_insert。如果您执行一个FLUSH TABLES语句或使用KILL thread_id进行删除,则会删除此线程。不过,在退出前,线程会首先把所有排入队列的行存储到表中。在这期间,该线程不会从其它线程处接受任何新的INSERT语句。如果您在此之后执行一个INSERT DELAYED语句,则会创建出一个新的管理程序线程。 注意,如果有一个INSERT DELAYED管理程序正在运行,则这意味着INSERT DELAYED语句比常规的INSERT语句具有更高的优先权。其它更新语句必须等待,直到INSERT DELAYED语句队列都运行完毕,或者管理程序线程被中止(使用KILL thread_id),或者执行了一个FLUSH TABLES时为止。 ·         以下状态变量提供了有关INSERT DELAYED语句的信息: <table border="1" cellpadding="0" id="table6"><tr><td> <p><strong> <span>状态变量</span></strong></p></td> <td> <p><strong> <span>意义</span></strong></p></td> </tr><tr><td> <p> <span>Delayed_insert_threads</span></p></td> <td> <p>管理程序线程的数目</p></td> </tr><tr><td> <p> <span>Delayed_writes</span></p></td> <td> <p><span>使用<span>INSERT DELAYED</span>写入的行的数目</span></p></td> </tr><tr><td> <p> <span>Not_flushed_delayed_rows</span></p></td> <td> <p>等待被写入的行的数目</p></td> </tr></table> ·         您可以通过发送一个SHOW STATUS语句,或者执行一个**mysqladmin extended-status**命令,来阅览这些变量。 注意,当没有使用表时,INSERT DELAYED比常规的INSERT要慢。对于服务器来说,为每个含有延迟行的表操纵一个独立的线程,也是一个额外的系统开销。这意味着只有当您确认您需要时,才应使用INSERT DELAYED。 ### 13.2.5. LOAD DATA INFILE语法 ~~~ LOAD DATA [LOW_PRIORITY | CONCURRENT] [LOCAL] INFILE 'file_name.txt' ~~~ ~~~     [REPLACE | IGNORE] ~~~ ~~~     INTO TABLE tbl_name ~~~ ~~~     [FIELDS ~~~ ~~~         [TERMINATED BY 'string'] ~~~ ~~~         [[OPTIONALLY] ENCLOSED BY 'char'] ~~~ ~~~         [ESCAPED BY 'char' ] ~~~ ~~~     ] ~~~ ~~~     [LINES ~~~ ~~~         [STARTING BY 'string'] ~~~ ~~~         [TERMINATED BY 'string'] ~~~ ~~~     ] ~~~ ~~~     [IGNORE number LINES] ~~~ ~~~     [(col_name_or_user_var,...)] ~~~ ~~~     [SET col_name = expr,...)] ~~~ LOAD DATA INFILE语句用于高速地从一个文本文件中读取行,并装入一个表中。文件名称必须为一个文字字符串。 要了解有关INSERT和LOAD DATA INFILE的效率的对比和有关LOAD DATA INFILE加速的更多信息,请参见[7.2.16节,“INSERT语句的速度”](# "7.2.16. Speed of INSERT Statements")。 由character_set_database系统变量指示的字符集被用于解释文件中的信息。SET NAMES和character_set_client的设置不会影响对输入的解释。 注意,目前不能载入UCS2数据文件。 您也可以通过使用**mysqlimport**应用程序载入数据文件;通过向服务器发送一个LOAD DATA INFILE语句实现此功能。--local选项用于使**mysqlimport**从客户主机中读取数据文件。如果客户端和服务器支持压缩协议,则您可以指定—compress选项提高在慢速网络中的性能。请参见[8.10节,“mysqlimport:数据导入程序](# "8.10. mysqlimport — A Data Import Program")。 如果您使用LOW_PRIORITY,则LOAD DATA语句的执行被延迟,直到没有其它的客户端从表中读取为止。 如果一个MyISAM表满足同时插入的条件(即该表在中间有空闲块),并且您对这个MyISAM表指定了CONCURRENT,则当LOAD DATA正在执行时,其它线程会从表中重新获取数据。即使没有其它线程在同时使用本表格,使用本选项也会略微影响LOAD DATA的性能。 如果指定了LOCAL,则被认为与连接的客户端有关: ·         如果指定了LOCAL,则文件会被客户主机上的客户端读取,并被发送到服务器。文件会被给予一个完整的路径名称,以指定确切的位置。如果给定的是一个相对的路径名称,则此名称会被理解为相对于启动客户端时所在的目录。 ·         如果LOCAL没有被指定,则文件必须位于服务器主机上,并且被服务器直接读取。 当在服务器主机上为文件定位时,服务器使用以下规则: ·         如果给定了一个绝对的路径名称,则服务器使用此路径名称。 ·         如果给定了带有一个或多个引导组件的相对路径名称,则服务器会搜索相对于服务器数据目录的文件。 ·         如果给定了一个不带引导组件的文件名称,则服务器会在默认数据库的数据库目录中寻找文件。 注意,这些规则意味着名为./myfile.txt的文件会从服务器数据目录中被读取,而名为myfile.txt的同样的文件会从默认数据库的数据库目录中读取。例如,下面的LOAD DATA语句会从db1数据库目录中读取文件data.txt,因为db1是当前数据库。即使语句明确把文件载入到db2数据库中的表里,也会从db1目录中读取。 ~~~ mysql> USE db1; ~~~ ~~~ mysql> LOAD DATA INFILE 'data.txt' INTO TABLE db2.my_table; ~~~ 注意,使用正斜杠指定Windows路径名称,而不是使用反斜杠。如果您使用反斜杠,您必须使用两个。 出于安全原因,当读取位于服务器中的文本文件时,文件必须位于数据库目录中,或者是全体可读的。另外,要对服务器文件使用LOAD DATA INFILE,您必须拥有FILE权限。 见[5.7.3节,“MySQL提供的权限”](# "5.7.3. Privileges Provided by MySQL")。 与让服务器直接读取文件相比,使用LOCAL速度略慢,这是因为文件的内容必须通过客户端发送到服务器上。不过,您不需要FILE权限来载入本地文件。 只有当您的服务器和您的客户端都许可时,LOCAL才可运行。例如,如果使用—local-infile=0启动**mysqld**,则LOCAL不运行。请参见[5.6.4节,“LOAD DATA LOCAL安全问题``”](# "5.6.4. Security Issues with LOAD DATA LOCAL")。 如果您需要LOAD DATA来从一个管道中读取,您可以使用以下方法(此处我们把/目录清单载入一个表格): ~~~ mkfifo /mysql/db/x/x ~~~ ~~~ chmod 666 /mysql/db/x/x ~~~ ~~~ find / -ls > /mysql/db/x/x ~~~ ~~~ mysql -e "LOAD DATA INFILE 'x' INTO TABLE x" x ~~~ 有些输入记录把原有的记录复制到唯一关键字值上。REPLACE和IGNORE关键字用于控制这些输入记录的操作。 如果您指定了REPLACE,则输入行会替换原有行(换句话说,与原有行一样,对一个主索引或唯一索引具有相同值的行)。请参见[13.2.6节,“REPLACE语法”](# "13.2.6. REPLACE Syntax")。 如果您指定IGNORE,则把原有行复制到唯一关键字值的输入行被跳过。如果您这两个选项都不指定,则运行情况根据LOCAL关键词是否被指定而定。不使用LOCAL时,当出现重复关键字值时,会发生错误,并且剩下的文本文件被忽略。使用LOCAL时,默认的运行情况和IGNORE被指定时的情况相同;这是因为在运行中间,服务器没有办法中止文件的传输。 如果您希望在载入运行过程中忽略外键的限制,您可以在执行LOAD DATA前发送一个SET FOREIGN_KEY_CHECKS=0语句。 如果您对一个空的MyISAM表使用LOAD DATA INFILE,则所有的非唯一索引会被创建在一个独立批中(对于REPAIR TABLE)。当您有许多索引时,这通常会使LOAD DATA INFILE大大加快。通常,LOAD DATA INFILE的速度会非常快,但是在某些极端情况下,您可以在把文件载入到表中之前使用ALTER TABLE...DISABLE KEYS关闭LOAD DATA INFILE,或者在载入文件之后使用ALTER TABLE...ENABLE KEYS再次创建索引,使创建索引的速度更快。请参见[7.2.16节,“INSERT语句的速度”](# "7.2.16. Speed of INSERT Statements")。 LOAD DATA INFILE是SELECT...INTO OUTFILE的补语。(见[13.2.7节,“SELECT语法”](# "13.2.7. SELECT Syntax")。)要从一个表中把数据写入一个文件中,应使用SELECT...INTO OUTFILE。要读取文件,放回到表中,应使用LOAD DATA INFILE。FIELDS和LINES子句的语法对于两个语句是一样的。两个子句都是自选的,但是如果两个都被指定了,FIELDS必须位于LINES的前面。 如果您指定了一个FIELDS子句,则每个亚子句(TERMINATED BY, [OPTIONALLY] ENCLOSED BY和ESCAPED BY)也是自选的。不过,您必须指定其中至少一个。 如果您不指定FIELDS子句,则默认值为假设您写下如下语句时的值: ~~~ FIELDS TERMINATED BY '\t' ENCLOSED BY '' ESCAPED BY '\\' ~~~ 如果您不指定LINES子句,则默认值为假设您写下如下语句时的值: ~~~ LINES TERMINATED BY '\n' STARTING BY '' ~~~ 换句话说,当读取输入值时,默认值会使LOAD DATA INFILE按如下方式运行: ·         在新行处寻找行的边界。 ·         不会跳过任何行前缀。 ·         在制表符处把行分解为字段。 ·         不希望字段被包含在任何引号字符之中。 ·         出现制表符、新行、或在‘\’前有‘\’时,理解为作为字段值一部分的文字字符。 相反的,当编写输出值时,默认值会使SELECT...INTO OUTFILE按如下方式运行: ·         在字段之间写入制表符。 ·         不把字段包含在任何引号字符中。 ·         当字段值中出现制表符、新行或‘\’时,使用‘\’进行转义。 ·         在行的末端写入新行。 注意,要写入FIELDS ESCAPED BY ‘\\’,您必须为待读取的值指定两个反斜杠,作为一个单反斜杠使用。 注释:如果您已经在Windows系统中生成了文本文件,您可能必须使用LINES TERMINATED BY ‘\r\n’来正确地读取文件,因为Windows程序通常使用两个字符作为一个行终止符。部分程序,比如**WordPad**,当编写文件时,可能会使用\r作为行终止符。要读取这样的文件,应使用LINES TERMINATED BY ‘\r’。 如果所有您希望读入的行都含有一个您希望忽略的共用前缀,则您可以使用'*prefix_string*'来跳过前缀(和前缀前的字符)。如果某行不包括前缀,则整个行被跳过。注释:*prefix_string*会出现在一行的中间。 示例: ~~~ mysql> LOAD DATA INFILE '/tmp/test.txt' ~~~ ~~~     -> INTO TABLE test LINES STARTING BY "xxx"; ~~~ 使用此语句,您可以读入包含有如下内容的文件: ~~~ xxx"row",1 ~~~ ~~~ something xxx"row",2 ~~~ 并只得到数据("row",1)和("row",2)。 IGNORE *number* LINES选项可以被用于在文件的开始处忽略行。例如,您可以使用IGNORE 1 LINES来跳过一个包含列名称的起始标题行: ~~~ mysql> LOAD DATA INFILE '/tmp/test.txt' ~~~ ~~~     -> INTO TABLE test IGNORE 1 LINES; ~~~ 当您联合使用SELECT...INTO OUTFILE和LOAD DATA INFILE来从一个数据库中把数据写入一个文件中,然后再读取文件,返回到数据库中时,用于两个语句的field-和line-handling选项必须匹配。否则,LOAD DATA INFILE不会正确地理解文件的内容。假设您使用SELECT...INTO OUTFILE来编写一个的文件,字段由逗号分隔: ~~~ mysql> SELECT * INTO OUTFILE 'data.txt' ~~~ ~~~     ->          FIELDS TERMINATED BY ',' ~~~ ~~~     ->          FROM table2; ~~~ 要读取由逗号分隔的文件并返回,则正确的语句应该是: ~~~ mysql> LOAD DATA INFILE 'data.txt' INTO TABLE table2 ~~~ ~~~     ->           FIELDS TERMINATED BY ','; ~~~ 如果您尝试使用以下所示的语句读入文件,则不会运行,因为该语句命令LOAD DATA INFILE寻找位于字段之间的制表符: ~~~ mysql> LOAD DATA INFILE 'data.txt' INTO TABLE table2 ~~~ ~~~     ->           FIELDS TERMINATED BY '\t'; ~~~ 结果很可能是,每个输入行被理解为一个单一字段。 LOAD DATA INFILE也可以被用于读取从外源中获取的文件。例如,一个dBASE格式的文件具有以逗号分隔并且包含在双引号中的字段。如果文件中的各行以新行为结尾,则此处所示的语句描述了您可以用于载入文件的field-和line-handling选项: ~~~ mysql> LOAD DATA INFILE 'data.txt' INTO TABLE tbl_name ~~~ ~~~     ->           FIELDS TERMINATED BY ',' ENCLOSED BY '"' ~~~ ~~~     ->           LINES TERMINATED BY '\n'; ~~~ 所有field-或line-handling选项都可以指定一个空字符串('')。如果字符串不是空的,则FIELDS [OPTIONALLY] ENCLOSED BY和FIELDS ESCAPED BY值必须为单一字符。FIELDS TERMINATED BY, LINES STARTING BY和LINES TERMINATED BY值可以超过一个字符。例如,要编写由回车/换行成对字符作为结尾的行,或读取包含这类行的文件,则应指定一个LINES TERMINATED BY ‘\r\n’子句。 如果jokes被由%%组成的行分隔,要读取包含jokes的文件,您可以这么操作: ~~~ mysql> CREATE TABLE jokes ~~~ ~~~     ->     (a INT NOT NULL AUTO_INCREMENT PRIMARY KEY, ~~~ ~~~     ->     joke TEXT NOT NULL); ~~~ ~~~ mysql> 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" ~~~ 如果您指定了OPTINALLY,则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字符作为前缀,对ENCLOSED BY字符进行转义。另外,要注意,如果您指定了一个空的ESCAPED BY值,则可能会生成不能被LOAD DATA INFILE正确读取的输出值。例如,如果转义符为空字符,则刚显示的先前输出值应显示如下。请观察,第四行中的第二个字段在引号后面包含一个逗号,该引号(错误地)显示出来,作为字段的结尾: ~~~ 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对输入值的解释没有影响。)如果ENCLOSED BY字符前面带有ESCAPED 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字符不是空字符,则可以在输出中用于对以下字符加前缀: ·         FIELDS ESCAPED BY字符 ·         FIELDS [OPTIONALLY] ENCLOSED BY字符 ·         FIELDS TERMINATED BY和LINES TERMINATED BY值的第一个字符 ·         ASCII 0(在转义符之后编写的字符实际上是ASCII‘0’,而不是一个值为0的字节) 如果FIELDS ESCAPED BY字符为空字符,则没有字符被转义,并且NULL被作为NULL输出,而不是\N。去指定一个空的转义符不是一个好办法,特别是如果数据的字段值包含任何刚给定的清单中的字符时,更不能这么做。 对于输入值,如果FIELDS ESCAPED BY字符不是空字符,则出现这种字符时会被剥离,然后以下字符被作为字段值的一部分。例外情况是,被转义的‘0’或‘N’(例如,\0或\N,此时转义符为‘\’)。这些序列被理解为ASCII NUL(一个零值字节)和NULL。用于NULL处理的规则在本节的后部进行说明。 要了解有关‘\’-escape语法的更多信息,请参见[9.1节,“文字值”](# "9.1. Literal Values")。 在特定情况下,field-和line-handling选项相互影响: ·         如果LINES TERMINATED BY是空字符串,并且FIELDS TERMINATED BY不是空字符串,则各行以FIELDS TERMINATED BY作为结尾。 ·         如果FIELDS TERMINATED BY和FIELDS ENCLOSED BY值均为空值(''),则使用固定行(无分隔)格式。使用固定行格式时,在字段之间不使用分隔符(但是您仍然可以有行终止符)。列值使用列的显示宽度进行写入和读取。例如,如果某列被定义为INT(7),则使用7字符字段写入列值。输出时,通过读取7个字符获取列值。 LINES TERMINATED BY仍然用于分隔行。如果某行不包含所有字段,则其余的各列被设置到默认值。如果您没有行终止符,您应该把终止符设置为''。在此情况下,文本文件必须包含每行的所有字段。 固定行格式也会影响NULL值的操作,这将在以后进行介绍。注意,如果您正在使用一个多字节字符集,则固定规格格式不会运行。 根据正在使用中的FIELDS和LINES选项的不同,NULL值的操作有所变化: ·         对于默认的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值和空字符串均无法辨别,这是因为两者都被作为空字符串写入。如果您需要在读取文件并返回时能够分辨两者,则您不应使用固定行格式。 LOAD DATA INFILE不支持有些情况: ·         固定规格行(FIELDS TERMINATED BY和FIELDS ENCLOSED BY均为空值)和BLOB或TEXT列。 ·         如果您指定了一个分隔符,并且该分隔符与其它的前缀一样,则LOAD DATA INFILE不能正确地理解输入值。例如,下面的FIELDS子句会导致问题: ~~~ ·                FIELDS TERMINATED BY '"' ENCLOSED BY '"' ~~~ ·         如果FIELDS ESCAPED BY为空值,则包含FIELDS ENCLOSED BY或LINES TERMINATED BY的字段值后面再接FIELDS TERMINATED BY值会导致LOAD DATA INFILE过早地停止读取一个字段或行。出现这种情况的原因是LOAD DATA INFILE不能正确地决定字段或行值在哪里结束。 以下的例子载入了persondata表中的所有列: ~~~ mysql> LOAD DATA INFILE 'persondata.txt' INTO TABLE persondata; ~~~ 默认情况下,如果在LOAD DATA INFILE语句的末尾处没有设列清单时,则输入行预计会包含一个字段,用于表中的每个列。如果您只想载入一个表的部分列,则应指定一个列清单: ~~~ mysql> LOAD DATA INFILE 'persondata.txt' ~~~ ~~~     ->           INTO TABLE persondata (col1,col2,...); ~~~ 如果输入文件中各字段的顺序与表中各列的顺序不同,您也必须指定一个列清单。否则,MySQL不能把输入字段和表中的列匹配起来。 列清单可以包含列名称或用户变量。支持SET子句。这使您可以把输入值赋予用户变量,然后在把结果赋予列之前,对这些值进行变换。 SET子句中的用户变量可以采用多种方式使用。以下例子使用数据文件中的第一列,直接用于t1.column1的值。在用户变量被用于t2.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赋值的右侧使用子查询。如果子查询可以返回一个值,并且此值将被赋予到一个列中,则此子查询只能是标量子查询。另外,您不能使用子查询从一个正在被载入的表中选择。 ·         对于于列/变量清单或SET子句,被IGNORE子句忽略的行不被处理。 ·         当载入采用固定行格式的数据时,不能使用用户变量,因为用户变量没有显示宽度。 当处理一个输入行时,LOAD DATA会依据列/变量清单和SET子句,把行拆分成字段,并使用值。然后,得到的行被插入表中。如果有用于表的BEFORE INSERT或AFTER INSERT触发器,则在插入行之前和插入行之后分别启动触发器。 如果一个输入行含有过多的字段,则多余的字段被忽略,并且警告的数量增加。 如果一个输入行含有的字段过少,则输入字段缺失的表中的列被设置为默认值。默认值赋值在[13.1.5节,“CREATE TABLE语法”](# "13.1.5. CREATE TABLE Syntax")中进行了说明。 如果字段值缺失,则对一个空字段值会被按不同方式理解: ·         对于字符串类型,列被设置为空字符串。 ·         对于数字类型,列被设置为0。 ·         对于日期和时间类型,列被设置为该类型相应的“zero”。请参见[11.3节,“日期和时间类型”](# "11.3. Date and Time Types")。 如果您明确地把一个空字符串赋予一个INSERT或UPDATE语句中的字符串类型、数字类型或日期或时间类型,则产生的这些值相同。 只有在两种情况下TIMESTAMP列被设置为当前日期和时间。一种情况时当列有一个NULL值(也就是\N)时;另一种情况是(仅对于第一个TIMESTAMP列),当一个字段清单被指定时,TIMESTAMP列会从字段清单中被略去。 LOAD DATA INFILE把所有的输入值当作字符串,所以您不能按照使用INSERT语句的方式使用ENUM或SET列的数字值。所有的ENUM和SET值必须被指定为字符串。 当LOAD DATA INFILE语句结束时,会按以下格式返回一个信息字符串: ~~~ Records: 1  Deleted: 0  Skipped: 0  Warnings: 0 ~~~ 如果您正在使用C API,您可以通过调用mysql_info()函数获取有关语句的信息。请参见[25.2.3.34节,“mysql_info()”](# "25.2.3.34. mysql_info()")。 当值通过INSERT语句被插入时或出现相同情况时,会发生警告(见[13.2.4节,“INSERT语法”](# "13.2.4. INSERT Syntax"))。例外情况是,当输入行中字段过多或过少时,LOAD DATA INFILE也生成警告。这些警告并不存储;警告的数量只用于指示运行是否良好。 您可以使用SHOW WARNINGS来得到第一批max_error_count警告的清单,作为有关运行错误的信息。请参见[13.5.4.22节,“SHOW WARNINGS语法”](# "13.5.4.22. SHOW WARNINGS Syntax")。 ### 13.2.6. REPLACE语法 ~~~ REPLACE [LOW_PRIORITY | DELAYED] ~~~ ~~~     [INTO] tbl_name [(col_name,...)] ~~~ ~~~     VALUES ({expr | DEFAULT},...),(...),... ~~~ 或: ~~~ REPLACE [LOW_PRIORITY | DELAYED] ~~~ ~~~     [INTO] tbl_name ~~~ ~~~     SET col_name={expr | DEFAULT}, ... ~~~ 或: ~~~ REPLACE [LOW_PRIORITY | DELAYED] ~~~ ~~~     [INTO] tbl_name [(col_name,...)] ~~~ ~~~     SELECT ... ~~~ REPLACE的运行与INSERT很相像。只有一点除外,如果表中的一个旧记录与一个用于PRIMARY KEY或一个UNIQUE索引的新记录具有相同的值,则在新记录被插入之前,旧记录被删除。请参见[13.2.4节,“INSERT语法”](# "13.2.4. INSERT Syntax")。 注意,除非表有一个PRIMARY KEY或UNIQUE索引,否则,使用一个REPLACE语句没有意义。该语句会与INSERT相同,因为没有索引被用于确定是否新行复制了其它的行。 所有列的值均取自在REPLACE语句中被指定的值。所有缺失的列被设置为各自的默认值,这和INSERT一样。您不能从当前行中引用值,也不能在新行中使用值。如果您使用一个例如“SET *col_name* = *col_name* + 1”的赋值,则对位于右侧的列名称的引用会被作为DEFAULT(*col_name*)处理。因此,该赋值相当于SET *col_name* = DEFAULT(*col_name*) + 1。 为了能够使用REPLACE,您必须同时拥有表的INSERT和DELETE权限。 REPLACE语句会返回一个数,来指示受影响的行的数目。该数是被删除和被插入的行数的和。如果对于一个单行REPLACE该数为1,则一行被插入,同时没有行被删除。如果该数大于1,则在新行被插入前,有一个或多个旧行被删除。如果表包含多个唯一索引,并且新行复制了在不同的唯一索引中的不同旧行的值,则有可能是一个单一行替换了多个旧行。 受影响的行数可以容易地确定是否REPLACE只添加了一行,或者是否REPLACE也替换了其它行:检查该数是否为1(添加)或更大(替换)。 如果您正在使用C API,则可以使用mysql_affected_rows()函数获得受影响的行数。 目前,您不能在一个子查询中,向一个表中更换,同时从同一个表中选择。 以下是所用算法的更详细的说明(该算法也用于LOAD DATA...REPLACE): 1.    尝试把新行插入到表中 2.    当因为对于主键或唯一关键字出现重复关键字错误而造成插入失败时: a.    从表中删除含有重复关键字值的冲突行 b.    再次尝试把新行插入到表中 ### 13.2.7. SELECT语法 [13.2.7.1. JOIN语法](#)[13.2.7.2. UNION语法  ](#) ~~~ SELECT ~~~ ~~~     [ALL | DISTINCT | DISTINCTROW ] ~~~ ~~~       [HIGH_PRIORITY] ~~~ ~~~       [STRAIGHT_JOIN] ~~~ ~~~       [SQL_SMALL_RESULT] [SQL_BIG_RESULT] [SQL_BUFFER_RESULT] ~~~ ~~~       [SQL_CACHE | SQL_NO_CACHE] [SQL_CALC_FOUND_ROWS] ~~~ ~~~     select_expr, ... ~~~ ~~~     [INTO OUTFILE 'file_name' export_options ~~~ ~~~       | INTO DUMPFILE 'file_name'] ~~~ ~~~     [FROM table_references ~~~ ~~~     [WHERE where_definition] ~~~ ~~~     [GROUP BY {col_name | expr | position} ~~~ ~~~       [ASC | DESC], ... [WITH ROLLUP]] ~~~ ~~~     [HAVING where_definition] ~~~ ~~~     [ORDER BY {col_name | expr | position} ~~~ ~~~       [ASC | DESC] , ...] ~~~ ~~~     [LIMIT {[offset,] row_count | row_count OFFSET offset}] ~~~ ~~~     [PROCEDURE procedure_name(argument_list)] ~~~ ~~~     [FOR UPDATE | LOCK IN SHARE MODE]] ~~~ SELECT用于恢复从一个或多个表中选择的行,并可以加入UNION语句和子查询。请参见[13.2.7.2节,“UNION语法 ”](# "13.2.7.2. UNION Syntax")和[13.2.8节,“Subquery语法”](# "13.2.8. Subquery Syntax")。 ·         每个*select_expr*都指示一个您想要恢复的列。 ·         * table_references*指示行从哪个表或哪些表中被恢复。在[13.2.7.1节,“JOIN语法”](# "13.2.7.1. JOIN Syntax")中对该语法进行了说明。 ·         * where_definition*包括关键词WHERE,后面接一个表达式。该表达式指示被选择的行必须满足的条件。 有的行在计算时未引用任何表。SELECT也可以用于恢复这类行。 举例说明: ~~~ mysql> SELECT 1 + 1; ~~~ ~~~         -> 2 ~~~ 所有被使用的子句必须按语法说明中显示的顺序严格地排序。例如,一个HAVING子句必须位于GROUP BY子句之后,并位于ORDER BY子句之前。 ·         使用AS *alias_name*可以为*select_expr*给定一个别名。此别名用作表达式的列名,可以用于GROUP BY、ORDER BY或HAVING子句。例如: ~~~ ·                mysql> SELECT CONCAT(last_name,', ',first_name) AS full_name ~~~ ~~~ ·                    -> FROM mytable ORDER BY full_name; ~~~ 在为*select_expr*给定别名时,AS关键词是自选的。前面的例子可以这样编写: ~~~ mysql> SELECT CONCAT(last_name,', ',first_name) full_name ~~~ ~~~     -> FROM mytable ORDER BY full_name; ~~~ 因为AS是自选的,如果您忘记在两个*select_expr*表达式之间加逗号,则会出现一个小问题:MySQL会把第二个表达式理解为一个别名。例如,在以下语句中,columnb被作为别名对待: ~~~ mysql> SELECT columna columnb FROM mytable; ~~~ 因此,使用AS明确地指定列的别名,把它作为习惯,是一个良好的操作规范。 ·         在一个WHERE子句中使用列别名是不允许的,因为当执行WHERE子句时,列值可能还没有被确定。请参见[A.5.4节,“与列别名有关的问题”](# "A.5.4. Problems with Column Aliases")。 ·         FROM * table_references*子句指示行从哪些表中被恢复。如果您命名的表多于一个,则您在进行一个联合操作。要了解有关联合语法的说明,请参见[13.2.7.1节,“JOIN语法”](# "13.2.7.1. JOIN Syntax")。对于每一个被指定的表,您可以自选地指定一个别名。 ~~~ ·                tbl_name [[AS] alias] ~~~ ~~~ ·                    [{USE|IGNORE|FORCE} INDEX (key_list)] ~~~ 使用USE INDEX、IGNORE INDEX、FORCE INDEX可以向优化符提示如何选择索引。这部分内容在[13.2.7.1节,“JOIN语法”](# "13.2.7.1. JOIN Syntax")中进行了讨论。 您可以使用SET max_seeks_for_key=*value*作为一种替代方法,来促使MySQL优先采用关键字扫描,替代表扫描。 ·         您可以把当前数据库中的一个表作为*tbl_name*(在当前数据库中)引用,或作为*db_name*.*tbl_name*引用,来明确地指定一个数据库。您可以把一列作为*col_name*, *tbl_name*.*col_name*引用或作为*db_name*.*tbl_name*.*col_name*引用。您不需要对一个列引用指定一个*tbl_name*或*db_name*.*tbl_name*前缀,除非此引用意义不明确。意义不明确时,要求指定明确的列引用格式。有关示例见[9.2节,“数据库、表、索引、列和别名”](# "9.2. Database, Table, Index, Column, and Alias Names")。 ·         在没有表被引用的情况下,允许您指定DUAL作为一个假的表名。 ~~~ ·                mysql> SELECT 1 + 1 FROM DUAL; ~~~ ~~~ ·                        -> 2 ~~~ 有些服务器要求一个FROM子句。DUAL仅用于与这些服务器兼容。如果没有表被引用,则MySQL不要求该子句,前面的语句可以按以下方法编写: ~~~ mysql> SELECT 1 + 1; ~~~ ~~~         -> 2 ~~~ ·         使用*tbl_name* AS *alias_name*或*tbl_name alias_name*可以为一个表引用起别名: ~~~ ·                mysql> SELECT t1.name, t2.salary FROM employee AS t1, info AS t2 ~~~ ~~~ ·                    ->     WHERE t1.name = t2.name; ~~~ ~~~ ·                mysql> SELECT t1.name, t2.salary FROM employee t1, info t2 ~~~ ~~~ ·                    ->     WHERE t1.name = t2.name; ~~~ ·         在WHERE子句中,您可以使用MySQL支持的所有函数,不过总计(总结)函数除外。请参见[第12章:](#)[*函数和操作符*](# "Chapter 12. Functions and Operators")。 ·         被选择的用于输出的列可以使用列名称、列别名或列位置被引用到ORDER BY和GROUP BY子句中。列位置为整数,从1开始: ~~~ ·                mysql> SELECT college, region, seed FROM tournament ~~~ ~~~ ·                    ->     ORDER BY region, seed; ~~~ ~~~ ·                mysql> SELECT college, region AS r, seed AS s FROM tournament ~~~ ~~~ ·                    ->     ORDER BY r, s; ~~~ ~~~ ·                mysql> SELECT college, region, seed FROM tournament ~~~ ~~~ ·                    ->     ORDER BY 2, 3; ~~~ 要以相反的顺序进行分类,应把DESC(降序)关键字添加到ORDER BY子句中的列名称中。默认值为升序;该值可以使用ASC关键词明确地指定。 不建议使用列位置,因为该语法已经从SQL标准中删除。 ·         如果您使用GROUP BY,则输出行根据GROUP BY列进行分类,如同您对相同的列进行了ORDER BY。MySQL对GROUP BY进行了扩展,因此您可以在各列(在子句中进行命名)的后面指定ASC和DESC: ~~~ ·                SELECT a, COUNT(b) FROM test_table GROUP BY a DESC ~~~ ·         MySQL对GROUP BY的使用进行了扩展,允许选择在GROUP BY子句中没有被提到的字段。如果您没有得到预期的结果,请阅读GROUP BY的说明,请参见[12.10节,“与GROUP BY子句同时使用的函数和修改程序``”](# "12.10. Functions and Modifiers for Use with GROUP BY Clauses")。 ·         GROUP BY允许一个WITH ROLLUP修饰符。请参见[12.10.2节,“GROUP BY修改程序”](# "12.10.2. GROUP BY Modifiers")。 ·         HAVING子句基本上是最后使用,只位于被发送给客户端的条目之前,没有进行优化。(LIMIT用于HAVING之后。) SQL标准要求HAVING必须引用GROUP BY子句中的列或用于总计函数中的列。不过,MySQL支持对此工作性质的扩展,并允许HAVING因为SELECT清单中的列和外部子查询中的列。 如果HAVING子句引用了一个意义不明确的列,则会出现警告。在下面的语句中,col2意义不明确,因为它既作为别名使用,又作为列名使用: ~~~ mysql> SELECT COUNT(col1) AS col2 FROM t GROUP BY col2 HAVING col2 = 2; ~~~ 标准SQL工作性质具有优先权,因此如果一个HAVING列名既被用于GROUP BY,又被用作输出列清单中的起了别名的列,则优先权被给予GROUP BY列中的列。 ·         HAVING不能用于应被用于WHERE子句的条目。例如,不能编写如下语句: ~~~ ·                mysql> SELECT col_name FROM tbl_name HAVING col_name > 0; ~~~ 而应这么编写: ~~~ mysql> SELECT col_name FROM tbl_name WHERE col_name > 0; ~~~ ·         HAVING子句可以引用总计函数,而WHERE子句不能引用: ~~~ ·                mysql> SELECT user, MAX(salary) FROM users ~~~ ~~~ ·                    ->     GROUP BY user HAVING MAX(salary)>10; ~~~ (在有些较早版本的MySQL中,本语句不运行。) ·         LIMIT子句可以被用于限制被SELECT语句返回的行数。LIMIT取一个或两个数字自变量,自变量必须是非负的整数常数(当使用已预备的语句时除外)。 使用两个自变量时,第一个自变量指定返回的第一行的偏移量,第二个自变量指定返回的行数的最大值。初始行的偏移量为0(不是1): ~~~ mysql> SELECT * FROM tbl LIMIT 5,10;  # Retrieve rows 6-15 ~~~ 为了与PostgreSQL兼容,MySQL也支持LIMIT *row_count* OFFSET *offset*语法。 如果要恢复从某个偏移量到结果集合的末端之间的所有的行,您可以对第二个参数是使用比较大的数。本语句可以恢复从第96行到最后的所有行: ~~~ mysql> SELECT * FROM tbl LIMIT 95,18446744073709551615; ~~~ 使用1个自变量时,该值指定从结果集合的开头返回的行数: ~~~ mysql> SELECT * FROM tbl LIMIT 5;     # Retrieve first 5 rows ~~~ 换句话说,LIMIT n与LIMIT 0,n等价。 对于已预备的语句,您可以使用位置保持符。以下语句将从tb1表中返回一行: ~~~ mysql> SET @a=1; ~~~ ~~~ mysql> PREPARE STMT FROM "SELECT * FROM tbl LIMIT ?"; ~~~ ~~~ mysql> EXECUTE STMT USING @a; ~~~ 以下语句将从tb1表中返回第二到第六行: ~~~ mysql> SET @skip=1; SET @numrows=5; ~~~ ~~~ mysql> PREPARE STMT FROM "SELECT * FROM tbl LIMIT ?, ?"; ~~~ ~~~ mysql> EXECUTE STMT USING @skip, @numrows; ~~~ ·         SELECT...INTO OUTFILE '*file_name*'形式的SELECT可以把被选择的行写入一个文件中。该文件被创建到服务器主机上,因此您必须拥有FILE权限,才能使用此语法。*file_name*不能是一个原有的文件。原有文件会阻止例如“/etc/passwd”的文件和数据库表被销毁。 SELECT...INTO OUTFILE语句的主要作用是让您可以非常快速地把一个表转储到服务器机器上。如果您想要在服务器主机之外的部分客户主机上创建结果文件,您不能使用SELECT...INTO OUTFILE。在这种情况下,您应该在客户主机上使用比如“mysql –e "SELECT ..." > file_name”的命令,来生成文件。 SELECT...INTO OUTFILE是LOAD DATA INFILE的补语;用于语句的exort_options部分的语法包括部分FIELDS和LINES子句,这些子句与LOAD DATA INFILE语句同时使用。请参见[13.2.5节,“LOAD DATA INFILE语法”](# "13.2.5. LOAD DATA INFILE Syntax")。 FIELDS ESCAPED BY用于控制如何写入特殊字符。如果FIELDS ESCAPED BY字符不是空字符,则被用于在输出中对以下字符设前缀: o        FIELDS ESCAPED BY字符 o        FIELDS [OPTIONALLY] ENCLOSED BY字符 o        FIELDS TERMINATED BY和LINES TERMINATED BY值的第一个字符 o        ASCII 0(在编写时接在转义符后面的是ASCII ‘0’,而不是一个零值字节) 如果FIELDS ESCAPED BY字符是空字符,则没有字符被转义,并且NULL被作为NULL输出,而不是作为\N输出。指定一个空的转义符不是一个好的主意。特别是当您的数据中的字段值包含刚被给予的清单中的字符时,更是如此。 其原因是您必须对所有FIELDS TERMINATED BY, ENCLOSED BY, ESCAPED BY或LINES TERMINATED BY字符进行转义,才能可靠地读取文件并返回。ASCII NUL被转义,以便更容易地使用调页程序观看。 生成的文件不必符合SQL语法,所以没有其它的字符需要被转义。 在下面的例子中,生成一个文件,各值用逗号隔开。这种格式可以被许多程序使用。 ~~~ SELECT a,b,a+b INTO OUTFILE '/tmp/result.text' ~~~ ~~~ FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '"' ~~~ ~~~ LINES TERMINATED BY '\n' ~~~ ~~~ FROM test_table; ~~~ ·         如果您使用INTO DUMPFILE代替INTO OUTFILE,则MySQL只把一行写入到文件中,不对任何列或行进行终止,也不执行任何转义处理。如果您想要把一个BLOB值存储到文件中,则这个语句是有用的。 ·         注释:任何由INTO OUTFILE或INTO DUMPFILE创建的文件都可以被服务器主机上的所有用户编写。原因是,MySQL服务器不能创建这样的文件,即文件的所有者不是该文件运行时所属的用户(任何时候,您都不能出于此原因或出于其它原因把**mysqld**作为根段运行)。该文件必须是全局可写的,这样您就可以操作其中的内容。 ·         有的过程应在结果集合内处理数据。PROCEDURE子句用于对这些过程进行命名。要了解示例,请参见[27.3.1节,“步骤分析”](# "27.3.1. Procedure Analyse")。 ·         存储引擎使用页面或行锁。如果您对存储引擎使用FOR UPDATE,则受到查询检验的行会被进行写锁定,直到当前事务结束为止。使用LOCK IN SHARE MODE可以设置一个共享锁。共享锁可以防止其它事务更新或删除被检验的行。请参见[15.2.10.5节,“锁定读取SELECT ... FOR UPDATE和SELECT ... LOCK IN SHARE MODE”](# "15.2.10.5. Locking Reads SELECT ... FOR UPDATE and SELECT ... LOCK IN SHARE MODE")。 在SELECT关键词的后面,您可以使用许多选项。这些选项可以影响语句的运行。 ALL, DISTINCT和DISTINCTROW选项指定是否重复行应被返回。如果这些选项没有被给定,则默认值为ALL(所有的匹配行被返回)。DISTINCT和DISTINCTROW是同义词,用于指定结果集合中的重复行应被删除。 HIGH_PRIORITY, STRAIGHT_JOIN和以SQL_为开头的选项是MySQL相对于标准SQL的扩展。 ·         HIGH_PRIORITY给予SELECT更高的优先权,高于用于更新表的语句。您应该只对查询使用HIGH_PRIORITY。查询速度非常快,而且立刻被执行。SELECT HIGH_PRIORITY查询在表被锁定用于读取时被发出。即使有一个新的语句正在等待表变为空闲,查询也会运行。 HIGH_PRIORITY不能和SELECT语句同时使用。SELECT语句是UNION的一部分。 ·         STRAIGHT_JOIN用于促使优化符把表联合在一起,顺序按照这些表在FROM子句中排列的顺序。如果优化符联合表时顺序不佳,您可以使用STRAIGHT_JOIN来加快查询的速度。请参见[7.2.1节,“EXPLAIN语法(获取关于SELECT的信息)``”](# "7.2.1. EXPLAIN Syntax (Get Information About a SELECT)")。STRAIGHT_JOIN也可以被用于*table_references*清单中。请参见[13.2.7.1节,“JOIN语法”](# "13.2.7.1. JOIN Syntax")。 ·         SQL_BIG_RESULT可以与GROUP BY或DISTINCT同时使用,来告知优化符结果集合有很多行。在这种情况下,MySQL直接使用以磁盘为基础的临时表(如果需要的话)。在这种情况下,MySQL还会优先进行分类,不优先使用临时表。临时表对于GROUP BY组分带有关键字。 ·         SQL_BUFFER_RESULT促使结果被放入一个临时表中。这可以帮助MySQL提前解开表锁定,在需要花费较长时间的情况下,也可以帮助把结果集合发送到客户端中。 ·         SQL_SMALL_RESULT可以与GROUP BY或DISTINCT同时使用,来告知优化符结果集合是较小的。在此情况下,MySAL使用快速临时表来储存生成的表,而不是使用分类。在MySQL 5.1中,通常不需要这样。 ·         SQL_CALC_FOUND_ROWS告知MySQL计算有多少行应位于结果集合中,不考虑任何LIMIT子句。行的数目可以使用SELECT FOUND_ROWS()恢复。请参见[12.9.3节,“信息函数”](# "12.9.3. Information Functions")。 ·         如果您正在使用一个query_cache_type值,值为2或DEMAND,则SQL_CACHE告知MySQL把查询结果存储在查询缓存中。对于使用UNION的查询或子查询,本选项会影响查询中的所有SELECT。请参见[5.13节,“MySQL查询高速缓冲”](# "5.13. The MySQL Query Cache")。 ·         SQL_NO_CACHE告知MySQL不要把查询结果存储在查询缓存中。请参见[5.13节,“MySQL查询高速缓冲”](# "5.13. The MySQL Query Cache")。对于一个使用UNION或子查询的查询,本选项会影响查询中的SELECT。 #### 13.2.7.1. JOIN语法 MySQL支持以下JOIN语法。这些语法用于SELECT语句的*table_references*部分和多表DELETE和UPDATE语句: ~~~ table_references: table_reference [, table_reference] … table_reference: table_factor | join_table table_factor: tbl_name [[AS] alias] [{USE|IGNORE|FORCE} INDEX (key_list)] | ( table_references ) | { OJ table_reference LEFT OUTER JOIN table_reference ON conditional_expr } join_table: table_reference [INNER | CROSS] JOIN table_factor [join_condition] | table_reference STRAIGHT_JOIN table_factor | table_reference STRAIGHT_JOIN table_factor ON condition | table_reference LEFT [OUTER] JOIN table_reference join_condition | table_reference NATURAL [LEFT [OUTER]] JOIN table_factor | table_reference RIGHT [OUTER] JOIN table_reference join_condition | table_reference NATURAL [RIGHT [OUTER]] JOIN table_factor join_condition: ON conditional_expr | USING (column_list) ~~~ 一个表引用还被称为一个联合表达式。 与SQL标准相比,*table_factor*的语法被扩展了。SQL标准只接受*table_reference*,而不是圆括号内的一系列条目。 如果我们把一系列*table_reference*条目中的每个逗号都看作相当于一个内部联合,则这是一个稳妥的扩展。例如: ~~~ SELECT * FROM t1 LEFT JOIN (t2, t3, t4) ~~~ ~~~                  ON (t2.a=t1.a AND t3.b=t1.b AND t4.c=t1.c) ~~~ 相当于: ~~~ SELECT * FROM t1 LEFT JOIN (t2 CROSS JOIN t3 CROSS JOIN t4) ~~~ ~~~                  ON (t2.a=t1.a AND t3.b=t1.b AND t4.c=t1.c) ~~~ 在MySQL中,CROSS JOIN从语法上说与INNER JOIN等同(两者可以互相替换。在标准SQL中,两者是不等同的。INNER JOIN与ON子句同时使用,CROSS JOIN以其它方式使用。 通常,在只含有内部联合运行的联合表达式中,圆括号可以被忽略。MySQL也支持嵌套的联合(见[7.2.10节,“MySQL如何优化嵌套Join”](# "7.2.10. How MySQL Optimizes Nested Joins"))。 通常,您不应对ON部分有任何条件。ON部分用于限定在结果集合中您想要哪些行。但是,您应在WHERE子句中指定这些条件。这条规则有一些例外。 在前面的清单中显示的{ OJ ... LEFT OUTER JOIN ...}语法的目的只是为了保持与ODBC的兼容性。语法中的花括号应按字面书写;该括号不是中间语法。中间语法用于语法描述的其它地方。 ·         表引用可以使用*tbl_name* AS *alias_name*或*tbl_name alias_name*指定别名: ~~~ ·                mysql> SELECT t1.name, t2.salary FROM employee AS t1, info AS t2 ~~~ ~~~ ·                    ->        WHERE t1.name = t2.name; ~~~ ~~~ ·                mysql> SELECT t1.name, t2.salary FROM employee t1, info t2 ~~~ ~~~ ·                    ->        WHERE t1.name = t2.name; ~~~ ·         ON条件句是可以被用于WHERE子句的格式的任何条件表达式。 ·         如果对于在LEFT JOIN中的ON或USING部分中的右表没有匹配的记录,则所有列被设置为NULL的一个行被用于右表。如果一个表在其它表中没有对应部分,您可以使用这种方法在这种表中查找记录: ~~~ ·                mysql> SELECT table1.* FROM table1 ~~~ ~~~ ·                    ->        LEFT JOIN table2 ON table1.id=table2.id ~~~ ~~~ ·                    ->        WHERE table2.id IS NULL; ~~~ 本例查找在table1中含有一个id值的所有行。同时,在table2中没有此id值(即,table1中的所有行在table2中没有对应的行)。本例假设table2.id被定义为NOT NULL。请参见[7.2.9节,“MySQL如何优化LEFT JOIN和RIGHT JOIN”](# "7.2.9. How MySQL Optimizes LEFT JOIN and RIGHT JOIN")。 ·         USING(*column_list*)子句用于为一系列的列进行命名。这些列必须同时在两个表中存在。如果表a和表b都包含列c1, c2和c3,则以下联合会对比来自两个表的对应的列: ~~~ ·                a LEFT JOIN b USING (c1,c2,c3) ~~~ ·         两个表的NATURAL [LEFT] JOIN被定义为与INNER JOIN语义相同,或与使用USING子句的LEFT JOIN语义相同。USING子句用于为同时存在于两个表中的所有列进行命名。 ·         INNER JOIN和,(逗号)在无联合条件下是语义相同的:两者都可以对指定的表计算出笛卡儿乘积(也就是说,第一个表中的每一行被联合到第二个表中的每一行)。 ·         RIGHT JOIN的作用与LEFT JOIN的作用类似。要使代码可以在数据库内移植,建议您使用LEFT JOIN代替RIGHT JOIN。 ·         STRAIGHT_JOIN与JOIN相同。除了有一点不一样,左表会在右表之前被读取。STRAIGH_JOIN可以被用于这样的情况,即联合优化符以错误的顺序排列表。 您可以提供提示,当从一个表中恢复信息时,MySQL应使用哪个索引。通过指定USE INDEX(key_list),您可以告知MySQL只使用一个索引来查找表中的行。另一种语法IGNORE INDEX(key_list)可以被用于告知MySQL不要使用某些特定的索引。如果EXPLAIN显示MySQL正在使用来自索引清单中的错误索引时,这些提示会有用处。 您也可以使用FORCE INDEX,其作用接近USE INDEX(key_list),不过增加了一项作用,一次表扫描被假设为代价很高。换句话说,只有当无法使用一个给定的索引来查找表中的行时,才使用表扫描。 USE KEY、IGNORE KEY和FORCE KEY是USE INDEX、IGNORE INDEX和FORCE INDEX的同义词。 注释:当MySQL决定如何在表中查找行并决定如何进行联合时,使用USE INDEX、IGNORE INDEX和FORCE INDEX只会影响使用哪些索引。当分解一个ORDER BY或GROUP BY时,这些语句不会影响某个索引是否被使用。 部分的联合示例: ~~~ mysql> SELECT * FROM table1,table2 WHERE table1.id=table2.id; ~~~ ~~~ mysql> SELECT * FROM table1 LEFT JOIN table2 ON table1.id=table2.id; ~~~ ~~~ mysql> SELECT * FROM table1 LEFT JOIN table2 USING (id); ~~~ ~~~ mysql> SELECT * FROM table1 LEFT JOIN table2 ON table1.id=table2.id ~~~ ~~~     ->          LEFT JOIN table3 ON table2.id=table3.id; ~~~ ~~~ mysql> SELECT * FROM table1 USE INDEX (key1,key2) ~~~ ~~~     ->          WHERE key1=1 AND key2=2 AND key3=3; ~~~ ~~~ mysql> SELECT * FROM table1 IGNORE INDEX (key3) ~~~ ~~~     ->          WHERE key1=1 AND key2=2 AND key3=3; ~~~ 见[7.2.9节,“MySQL如何优化LEFT JOIN和RIGHT JOIN”](# "7.2.9. How MySQL Optimizes LEFT JOIN and RIGHT JOIN")。 注释:自然联合和使用USING的联合,包括外部联合变量,依据SQL:2003标准被处理。这些变更时MySQL与标准SQL更加相符。不过,对于有些联合,这些变更会导致不同的输出列。另外,有些查询在旧版本(5.0.12以前)工作正常,但也必须重新编写,以符合此标准。对于有关当前联合处理和旧版本中的联合处理的效果的对比,以下列表提供了更详细的信息。 ·         NATURAL联合或USING联合的列会与旧版本不同。特别是,不再出现冗余的输出列,用于SELECT *扩展的列的顺序会与以前不同。 示例: ~~~ CREATE TABLE t1 (i INT, j INT); ~~~ ~~~ CREATE TABLE t2 (k INT, j INT); ~~~ ~~~ INSERT INTO t1 VALUES(1,1); ~~~ ~~~ INSERT INTO t2 VALUES(1,1); ~~~ ~~~ SELECT * FROM t1 NATURAL JOIN t2; ~~~ ~~~ SELECT * FROM t1 JOIN t2 USING (j); ~~~ 对于旧版本,语句会产生以下输出: ~~~ +------+------+------+------+ ~~~ ~~~ | i    | j    | k    | j    | ~~~ ~~~ +------+------+------+------+ ~~~ ~~~ |    1 |    1 |    1 |    1 | ~~~ ~~~ +------+------+------+------+ ~~~ ~~~ +------+------+------+------+ ~~~ ~~~ | i    | j    | k    | j    | ~~~ ~~~ +------+------+------+------+ ~~~ ~~~ |    1 |    1 |    1 |    1 | ~~~ ~~~ +------+------+------+------+ ~~~ 在第一个SELECT语句中,列i同时出现在两个表中,为一个联合列,所以,依据标准SQL,该列在输出中只出现一次。与此类似,在第二个SELECT语句中,列j在USING子句中被命名,应在输出中只出现一次。但是,在两种情况下,冗余的列均没被消除。另外,依据标准SQL,列的顺序不正确。 现在,语句产生如下输出: ~~~ +------+------+------+ ~~~ ~~~ | j    | i    | k    | ~~~ ~~~ +------+------+------+ ~~~ ~~~ |    1 |    1 |    1 | ~~~ ~~~ +------+------+------+ ~~~ ~~~ +------+------+------+ ~~~ ~~~ | j    | i    | k    | ~~~ ~~~ +------+------+------+ ~~~ ~~~ |    1 |    1 |    1 | ~~~ ~~~ +------+------+------+ ~~~ 冗余的列被消除,并且依据标准SQL,列的顺序是正确的: o        第一,两表共有的列,按在第一个表中的顺序排列 o        第二,第一个表中特有的列,按该表中的顺序排列 o        第三,第二个表中特有的列,按该表中的顺序排列 ·         对多方式自然联合的估算会不同。方式要求重新编写查询。假设您有三个表t1(a,b), t2(c,b)和t3(a,c),每个表有一行:t1(1,2), t2(10,2)和t3(7,10)。同时,假设这三个表具有NATURAL JOIN: ~~~ ·                SELECT … FROM t1 NATURAL JOIN t2 NATURAL JOIN t3; ~~~ 在旧版本中,第二个联合的左操作数被认为是t2,然而它应该为嵌套联合(t1 NATURAL JOIN t2)。结果,对t3的列进行检查时,只检查其在t2中的共有列。如果t3与t1有共有列,这些列不被用作equi-join列。因此,在旧版本的MySQL中,前面的查询被转换为下面的equi-join: ~~~ SELECT … FROM t1, t2, t3 ~~~ ~~~   WHERE t1.b = t2.b AND t2.c = t3.c; ~~~ 此联合又省略了一个equi-join谓语(t1.a = t3.a)。结果是,该联合产生一个行,而不是空结果。正确的等价查询如下: ~~~ SELECT … FROM t1, t2, t3 ~~~ ~~~   WHERE t1.b = t2.b AND t2.c = t3.c AND t1.a = t3.a; ~~~ 如果您要求在当前版本的MySQL中获得和旧版本中相同的查询结果,应把自然联合改写为第一个equi-join。 ·         在旧版本中,逗号操作符(,)和JOIN均有相同的优先权,所以联合表达式t1, t2 JOIN t3被理解为((t1, t2) JOIN t3)。现在,JOIN有更高的优先权,所以表达式被理解为(t1, (t2 JOIN t3))。这个变更会影响使用ON子句的语句,因为该子句只参阅联合操作数中的列。优先权的变更改变了对什么是操作数的理解。 示例: ~~~ CREATE TABLE t1 (i1 INT, j1 INT); ~~~ ~~~ CREATE TABLE t2 (i2 INT, j2 INT); ~~~ ~~~ CREATE TABLE t3 (i3 INT, j3 INT); ~~~ ~~~ INSERT INTO t1 VALUES(1,1); ~~~ ~~~ INSERT INTO t2 VALUES(1,1); ~~~ ~~~ INSERT INTO t3 VALUES(1,1); ~~~ ~~~ SELECT * FROM t1, t2 JOIN t3 ON (t1.i1 = t3.i3); ~~~ 在旧版本中,SELECT是合法的,因为t1, t2被隐含地归为(t1,t2)。现在,JOIN取得了优先权,因此用于ON子句的操作数是t2和t3。因为t1.i1不是任何一个操作数中的列,所以结果是出现在'on clause'中有未知列't1.i1'的错误。要使联合可以被处理,用使用圆括号把前两个表明确地归为一组,这样用于ON子句的操作数为(t1,t2)和t3: ~~~ SELECT * FROM (t1, t2) JOIN t3 ON (t1.i1 = t3.i3); ~~~ 本变更也适用于INNER JOIN,CROSS JOIN,LEFT JOIN和RIGHT JOIN。 ·         在旧版本中,ON子句可以参阅在其右边命名的表中的列。现在,ON子句只能参阅操作数。 示例: ~~~ CREATE TABLE t1 (i1 INT); ~~~ ~~~ CREATE TABLE t2 (i2 INT); ~~~ ~~~ CREATE TABLE t3 (i3 INT); ~~~ ~~~ SELECT * FROM t1 JOIN t2 ON (i1 = i3) JOIN t3; ~~~ 在旧版本中,SELECT语句是合法的。现在该语句会运行失败,出现在'on clause'中未知列'i3'的错误。这是因为i3是t3中的一个表,而t3不是ON子句中的操作数。本语句应进行如下改写: ~~~ SELECT * FROM t1 JOIN t2 JOIN t3 ON (i1 = i3); ~~~ ·         在旧版本中,一个USING子句可以被改写为一个ON子句。ON子句对比了相应的列。例如,以下两个子句具有相同的语义: ~~~ ·                a LEFT JOIN b USING (c1,c2,c3) ~~~ ~~~ ·                a LEFT JOIN b ON a.c1=b.c1 AND a.c2=b.c2 AND a.c3=b.c3 ~~~ 现在,这两个子句不再是一样的: o        在决定哪些行满足联合条件时,两个联合保持语义相同。 o        在决定哪些列显示SELECT *扩展时,两个联合的语义不相同。USING联合选择对应列中的合并值,而ON联合选择所有表中的所有列。对于前面的USING联合,SELECT *选择这些值: ~~~ o                     COALESCE(a.c1,b.c1), COALESCE(a.c2,b.c2), COALESCE(a.c3,b.c3) ~~~ 对于ON联合,SELECT *选择这些值: ~~~ a.c1, a.c2, a.c3, b.c1, b.c2, b.c3 ~~~ 使用内部联合时,COALESCE(a.c1,b.c1)与a.c1或b.c1相同,因为两列将具有相同的值。使用外部联合时(比如LEFT JOIN),两列中有一列可以为NULL。该列将会从结果中被忽略。 #### 13.2.7.2. UNION语法   ~~~ SELECT ... ~~~ ~~~ UNION [ALL | DISTINCT] ~~~ ~~~ SELECT ... ~~~ ~~~ [UNION [ALL | DISTINCT] ~~~ ~~~ SELECT ...] ~~~ UNION用于把来自许多SELECT语句的结果组合到一个结果集合中。 列于每个SELECT语句的对应位置的被选择的列应具有相同的类型。(例如,被第一个语句选择的第一列应和被其它语句选择的第一列具有相同的类型。)在第一个SELECT语句中被使用的列名称也被用于结果的列名称。 SELECT语句为常规的选择语句,但是受到如下的限定: ·         只有最后一个SELECT语句可以使用INTO OUTFILE。 ·         HIGH_PRIORITY不能与作为UNION一部分的SELECT语句同时使用。如果您对第一个SELECT指定了HIGH_PRIORITY,则不会起作用。如果您对其它后续的SELECT语句指定了HIGH_PRIORITY,则会产生语法错误。 如果您对UNION不使用关键词ALL,则所有返回的行都是唯一的,如同您已经对整个结果集合使用了DISTINCT。如果您指定了ALL,您会从所有用过的SELECT语句中得到所有匹配的行。 DISTINCT关键词是一个自选词,不起任何作用,但是根据SQL标准的要求,在语法中允许采用。(在MySQL中,DISTINCT代表一个共用体的默认工作性质。) 您可以在同一查询中混合UNION ALL和UNION DISTINCT。被混合的UNION类型按照这样的方式对待,即DISTICT共用体覆盖位于其左边的所有ALL共用体。DISTINCT共用体可以使用UNION DISTINCT明确地生成,或使用UNION(后面不加DISTINCT或ALL关键词)隐含地生成。 如果您想使用ORDER BY或LIMIT子句来对全部UNION结果进行分类或限制,则应对单个地SELECT语句加圆括号,并把ORDER BY或LIMIT放到最后一个的后面。以下例子同时使用了这两个子句: ~~~ (SELECT a FROM tbl_name WHERE a=10 AND B=1) ~~~ ~~~ UNION ~~~ ~~~ (SELECT a FROM tbl_name WHERE a=11 AND B=2) ~~~ ~~~ ORDER BY a LIMIT 10; ~~~ 这种ORDER BY不能使用包括表名称(也就是,采用*tbl_name*.*col_name*格式的名称)列引用。可以在第一个SELECT语句中提供一个列别名,并在ORDER BY中参阅别名,或使用列位置在ORDER BY中参阅列。(首选采用别名,因为不建议使用列位置。) 另外,如果带分类的一列有别名,则ORDER BY子句必须引用别名,而不能引用列名称。以下语句中的第一个语句必须运行,但是第二个会运行失败,出现在'order clause'中有未知列'a'的错误: ~~~ (SELECT a AS b FROM t) UNION (SELECT ...) ORDER BY b; ~~~ ~~~ (SELECT a AS b FROM t) UNION (SELECT ...) ORDER BY a; ~~~ To apply ORDER BY or LIMIT to an individual SELECT, place the clause inside the parentheses that enclose the SELECT:   为了对单个SELECT使用ORDER BY或LIMIT,应把子句放入圆括号中。圆括号包含了SELECT: ~~~ (SELECT a FROM tbl_name WHERE a=10 AND B=1 ORDER BY a LIMIT 10) ~~~ ~~~ UNION ~~~ ~~~ (SELECT a FROM tbl_name WHERE a=11 AND B=2 ORDER BY a LIMIT 10); ~~~ 圆括号中用于单个SELECT语句的ORDER BY只有当与LIMIT结合后,才起作用。否则,ORDER BY被优化去除。 UNION结果集合中的列的类型和长度考虑了被所有SELECT语句恢复的数值。例如,考虑如下语句: ~~~ mysql> SELECT REPEAT('a',1) UNION SELECT REPEAT('b',10); ~~~ ~~~ +---------------+ ~~~ ~~~ | REPEAT('a',1) | ~~~ ~~~ +---------------+ ~~~ ~~~ | a             | ~~~ ~~~ | bbbbbbbbbb    | ~~~ ~~~ +---------------+ ~~~ (在部分早期版本的MySQL中,第二行已被删节到长度为1。) ### 13.2.8. Subquery语法 [ 13.2.8.1. 子查询作为标量操作数](#)[13.2.8.2. 使用子查询进行比较](#)[13.2.8.3. 使用ANY, IN和SOME进行子查询](#)[13.2.8.4. 使用ALL进行子查询``](#)[ 13.2.8.5. 行子查询](#)[13.2.8.6. EXISTS和NOT EXISTS](#)[ 13.2.8.7. 关联子查询](#)[ 13.2.8.8. FROM子句中的子查询``](#)[13.2.8.9. 子查询错误](#)[ 13.2.8.10. 优化子查询](#)[ 13.2.8.11. 把子查询作为用于早期MySQL版本的联合进行改写](#) 子查询是另一个语句中的一个SELECT语句。 MySQL支持SQL标准要求的所有子查询格式和操作,也支持MySQL特有的几种特性。 以下是一个子查询的例子: ~~~ SELECT * FROM t1 WHERE column1 = (SELECT column1 FROM t2); ~~~ 在本例中,SELECT * FROM t1...是外部查询(或外部语句),(SELECT column1 FROM t2)是子查询。我们可以说子查询嵌套在外部查询中。实际上,子查询也可以嵌套在其它子查询中,嵌套程度可以很深。子查询必须要位于圆括号中。 子查询的主要优势为: ·         子查询允许结构化的查询,这样就可以把一个语句的每个部分隔离开。 ·         有些操作需要复杂的联合和关联。子查询提供了其它的方法来执行这些操作。 ·         在许多人看来,子查询是可读的。实际上,子查询给予人们调用早期SQL“结构化查询语言”的原本的想法,这是子查询的创新。 以下是一个示例语句。该语句显示了有关子查询语法的要点。子查询语法由SQL标准指定并被MySQL支持。 ~~~ DELETE FROM t1 ~~~ ~~~ WHERE s11 > ANY ~~~ ~~~ (SELECT COUNT(*) /* no hint */ FROM t2 ~~~ ~~~ WHERE NOT EXISTS ~~~ ~~~ (SELECT * FROM t3 ~~~ ~~~ WHERE ROW(5*t2.s1,77)= ~~~ ~~~ (SELECT 50,11*s1 FROM t4 UNION SELECT 50,77 FROM ~~~ ~~~ (SELECT * FROM t5) AS t5))); ~~~ 一个子查询会返回一个标量(单一值)、一个行、一个列或一个表(一行或多行及一列或多列)。这些子查询被称为标量、列、行和表子查询。可返回一个特定种类结果的子查询经常只被用于特定的语境中,在后面各节中有说明。 有些语句可以使用子查询。对这些语句的类型基本没有限定。子查询可以包括普通SELECT可以包括的任何关键词或子句:DISTINCT, GROUP BY, ORDER BY, LIMIT, 联合, 索引提示, UNION结构化, 评注和函数等。 有一个限定是,一个子查询的外部语句必须是以下语句之一:SELECT, INSERT, UPDATE, DELETE, SET或DO。还有一个限定是,目前,您不能在一个子查询中修改一个表,又在同一个表中选择。这适用于DELETE, INSERT, REPLACE和UPDATE语句。在[附录](#)[I:*特性限制*](# "Appendix I. Feature Restrictions")中给出了对子查询使用的更综合的讨论。 #### 13.2.8.1. 子查询作为标量操作数 子查询最简单的形式是返回单一值的标量子查询。标量子查询是一个单一操作数。只要单一列值或文字是合法的,并且您希望子查询具有所有操作数都具有的特性,则您就可以使用子查询。操作数具有的特性包括:一个数据类型、一个长度、一个指示是否可以为NULL的标志等。举例说明: ~~~ CREATE TABLE t1 (s1 INT, s2 CHAR(5) NOT NULL); ~~~ ~~~ INSERT INTO t1 VALUES(100, 'abcde'); ~~~ ~~~ SELECT (SELECT s2 FROM t1); ~~~ 在本SELECT中的子查询返回一个单一值('abcde')。该单一值的数据类型为CHAR,长度为5,字符集和整序与在CREATE TABLE时有效的默认值相同,并有一个指示符号,指示列中的值可以为NULL。实际上,基本上所有的子查询都为NULL。如果在本例中使用的表为空表,则子查询的值应为NULL。 在有些情况下,标量子查询不能使用。如果一个语句只允许一个文字值,您不能使用子查询。例如,LIMIT要求文字整数自变量,LOAD DATA要求一个文字字符串文件名。您不能使用子查询来提供这些值。 后面各节包括更简练的结构(SELECT column1 FROM t1)。当您在这些章节中观看例子时,请设想一下您自己的代码包含更多样、更复杂的结构。 举例说明,假设我们制作两个表: ~~~ CREATE TABLE t1 (s1 INT); ~~~ ~~~ INSERT INTO t1 VALUES (1); ~~~ ~~~ CREATE TABLE t2 (s1 INT); ~~~ ~~~ INSERT INTO t2 VALUES (2); ~~~ 然后执行一个SELECT: ~~~ SELECT (SELECT s1 FROM t2) FROM t1; ~~~ 结果为2,因为t2中有一行包含s1,s1有一个值为2。 一个标量子查询可以为一个表达式的一部分。不要忘记圆括号。即使是子查询是一个为函数提供自变量的操作数时,也不要忘记圆括号。举例说明: ~~~ SELECT UPPER((SELECT s1 FROM t1)) FROM t2; ~~~ #### 13.2.8.2. 使用子查询进行比较 子查询最常见的一种使用方式如下: ~~~ non_subquery_operand comparison_operator (subquery) ~~~ 当*comparison_operator*是以下 操作符之一时: ~~~ =  >  <  >=  <=  <> ~~~ 例如: ~~~   ... 'a' = (SELECT column1 FROM t1) ~~~ 有时,子查询的合法位置只能在比较式的右侧,您可以发现,在有些旧的DBMSs中仍保持这一点。 以下是一个常见格式的子查询比较的例子。您不能使用联合进行此类比较。表t1中有些值与表t2中的最大值相同。该比较可以查找出所有这类值: ~~~ SELECT column1 FROM t1 ~~~ ~~~ WHERE column1 = (SELECT MAX(column2) FROM t2); ~~~ 下面还有另一个例子,该例子也不可能使用联合,因为该例子涉及对其中一个表进行总计。表t1中的有些行含有的值会在给定的列中出现两次。该例子可以查找出所有这些行: ~~~ SELECT * FROM t1 AS t ~~~ ~~~ WHERE 2 = (SELECT COUNT(*) FROM t1 WHERE t1.id = t.id); ~~~ 对于采用这些操作符之一进行的比较,子查询必须返回一个标量。有一个例外,那就是=可以和行子查询同时使用。请参见[13.2.8.5节,“行子查询”](# "13.2.8.5. Row Subqueries")。 #### 13.2.8.3. 使用ANY, IN和SOME进行子查询 语法: ~~~ operand comparison_operator ANY (subquery) ~~~ ~~~ operand IN (subquery) ~~~ ~~~ operand comparison_operator SOME (subquery) ~~~ ANY关键词必须后面接一个比较操作符。ANY关键词的意思是“对于在子查询返回的列中的任一数值,如果比较结果为TRUE的话,则返回TRUE”。例如: ~~~ SELECT s1 FROM t1 WHERE s1 > ANY (SELECT s1 FROM t2); ~~~ 假设表t1中有一行包含(10)。如果表t2包含(21,14,7),则表达式为TRUE,因为t2中有一个值为7,该值小于10。如果表t2包含(20,10),或者如果表t2为空表,则表达式为FALSE。如果表t2包含(NULL, NULL, NULL),则表达式为UNKNOWN。 词语IN是=ANY的别名。因此,这两个语句是一样的: ~~~ SELECT s1 FROM t1 WHERE s1 = ANY (SELECT s1 FROM t2); ~~~ ~~~ SELECT s1 FROM t1 WHERE s1 IN    (SELECT s1 FROM t2); ~~~ 不过,NOT IN不是<> ANY的别名,但是是<> ALL的别名。请参见[13.2.8.4节,“使用ALL进行子查询``”](# "13.2.8.4. Subqueries with ALL")。 词语SOME是ANY的别名。因此,这两个语句是一样的: ~~~ SELECT s1 FROM t1 WHERE s1 <> ANY  (SELECT s1 FROM t2); ~~~ ~~~ SELECT s1 FROM t1 WHERE s1 <> SOME (SELECT s1 FROM t2); ~~~ 使用词语SOME的机会很少,但是本例显示了为什么SOME是有用的。对于多数人来说,英语短语“a is not equal to any b”的意思是“没有一个b与a相等”,但是在SQL语法中不是这个意思。该语法的意思是“有部分b与a不相等”。使用<> SOME有助于确认每个人都理解该查询的真正含义。 #### 13.2.8.4. 使用ALL进行子查询`` 语法: ~~~ operand comparison_operator ALL (subquery) ~~~ 词语ALL必须接在一个比较操作符的后面。ALL的意思是“对于子查询返回的列中的所有值,如果比较结果为TRUE,则返回TRUE。”例如: ~~~ SELECT s1 FROM t1 WHERE s1 > ALL (SELECT s1 FROM t2); ~~~ 假设表1中有一行包含(10)。如果表t2包含(-5,0,+5),则表达式为TRUE,因为10比t2中的所有三个值都大。如果表t2包含(12,6,NULL,-100),则表达式为FALSE,因为表t2中有一个值12大于10。如果表t2包含(0,NULL,1),则表达式为unknown。 最后,如果表t2为空表,则结果为TRUE。因此,当表t2为空表时,以下语句为TRUE: ~~~ SELECT * FROM t1 WHERE 1 > ALL (SELECT s1 FROM t2); ~~~ 但是,当表t2为空表时,本语句为NULL: ~~~ SELECT * FROM t1 WHERE 1 > (SELECT s1 FROM t2); ~~~ 另外,当表t2为空表时,以下语句为NULL: ~~~ SELECT * FROM t1 WHERE 1 > ALL (SELECT MAX(s1) FROM t2); ~~~ 通常,包含NULL值的表和空表为“边缘情况”。当编写子查询代码时,都要考虑您是否把这两种可能性计算在内。 NOT IN是<> ALL的别名。因此,以下两个语句是相同的: ~~~ SELECT s1 FROM t1 WHERE s1 <> ALL (SELECT s1 FROM t2); ~~~ ~~~ SELECT s1 FROM t1 WHERE s1 NOT IN (SELECT s1 FROM t2); ~~~ #### 13.2.8.5. 行子查询 对于本点的讨论属于标量或列子查询,即返回一个单一值或一列值的子查询。行子查询是一个能返回一个单一行的子查询变量,因此可以返回一个以上的列值。以下是两个例子: ~~~ SELECT * FROM t1 WHERE (1,2) = (SELECT column1, column2 FROM t2); ~~~ ~~~ SELECT * FROM t1 WHERE ROW(1,2) = (SELECT column1, column2 FROM t2); ~~~ 如果在表t2的一个行中,column1=1并且column2=2,则查询结果均为TRUE。 表达式(1,2)和ROW(1,2)有时被称为行构造符。两者是等同的,在其它的语境中,也是合法的。例如,以下两个语句在语义上是等同的(但是目前只有第二个语句可以被优化): ~~~   SELECT * FROM t1 WHERE (column1,column2) = (1,1); ~~~ ~~~   SELECT * FROM t1 WHERE column1 = 1 AND column2 = 1; ~~~ 行构造符通常用于与对能返回两个或两个以上列的子查询进行比较。例如,以下查询可以答复请求,“在表t1中查找同时也存在于表t2中的所有的行”: ~~~ SELECT column1,column2,column3 ~~~ ~~~ FROM t1 ~~~ ~~~ WHERE (column1,column2,column3) IN ~~~ ~~~ (SELECT column1,column2,column3 FROM t2); ~~~ #### 13.2.8.6. EXISTS和NOT EXISTS 如果一个子查询返回任何的行,则EXISTS *subquery*为FALSE。例如: ~~~ SELECT column1 FROM t1 WHERE EXISTS (SELECT * FROM t2); ~~~ 过去,EXISTS子查询以SELECT *为开始,但是可以以SELECT 5或SELECT column1或其它的为开始。MySQL在这类子查询中忽略了SELECT清单,因此没有区别。 对于前面的例子,如果t2包含任何行,即使是只含有NULL值的行,EXISTS条件也为TRUE。这实际上是一个不可能的例子,因为基本上所有的[NOT] EXISTS子查询均包含关联。以下是一些更现实的例子: ·         哪些种类的商店出现在一个或多个城市里? ~~~ ·                SELECT DISTINCT store_type FROM stores ~~~ ~~~ ·                  WHERE EXISTS (SELECT * FROM cities_stores ~~~ ~~~ ·                                WHERE cities_stores.store_type = stores.store_type); ~~~ ·         哪些种类的商店没有出现在任何城市里? ~~~ ·                SELECT DISTINCT store_type FROM stores ~~~ ~~~ ·                  WHERE NOT EXISTS (SELECT * FROM cities_stores ~~~ ~~~ ·                                    WHERE cities_stores.store_type = stores.store_type); ~~~ ·         哪些种类的商店出现在所有城市里? ~~~ ·                SELECT DISTINCT store_type FROM stores s1 ~~~ ~~~ ·                  WHERE NOT EXISTS ( ~~~ ~~~ ·                    SELECT * FROM cities WHERE NOT EXISTS ( ~~~ ~~~ ·                      SELECT * FROM cities_stores ~~~ ~~~ ·                       WHERE cities_stores.city = cities.city ~~~ ~~~ ·                       AND cities_stores.store_type = stores.store_type)); ~~~ 最后一个例子是一个双嵌套NOT EXISTS查询。也就是,该查询包含一个NOT EXISTS子句,该子句又包含在一个NOT EXISTS子句中。该查询正式地回答了这个问题,“是否有某个城市拥有没有列在Stores中的商店?”。可以比较容易的说,一个带嵌套的NOT EXISTS可以回答这样的问题,“是否对于所有的y,x都为TRUE?” #### 13.2.8.7. 关联子查询 相关联的子查询是一个包含对表的引用的子查询。该表也显示在外部查询中。例如: ~~~ SELECT * FROM t1 WHERE column1 = ANY ~~~ ~~~ (SELECT column1 FROM t2 WHERE t2.column2 = t1.column2); ~~~ 注意,即使子查询的FROM子句不提及表t1,该子查询也会包含一个对t1中一列的引用。所以,MySQL看上去位于子查询的外部,并在外部查询中查找t1。 假设表t1包含一行,在此行中column1=5并且column2=6;同时,表t2包含一行,在此行中column1=5并且column2=7。简单的表达式... WHERE column1 = ANY (SELECT column1 FROM t2)会为TRUE。但是在本例中,在子查询中的WHERE子句为FALSE(因为(5,6)不等于(5,7)),所以子查询总体上为FALSE。 范围划分规则:MySQL从内到外进行评估。例如: ~~~ SELECT column1 FROM t1 AS x ~~~ ~~~ WHERE x.column1 = (SELECT column1 FROM t2 AS x ~~~ ~~~ WHERE x.column1 = (SELECT column1 FROM t3 ~~~ ~~~ WHERE x.column2 = t3.column1)); ~~~ 在本语句中,x.column2必须是表t2中的列,因为SELECT column1 FROM t2 AS x ...对t2进行了重命名。它不是表t1中的列,因为SELECT column1 FROM t1 ...是一个更靠外的外部查询。 对于HAVING或ORDER BY子句中的子查询,MySQL也会在外部选择清单中寻找列名称。 对于特定的情况,相关联的子查询被优化。例如: ~~~ val IN (SELECT key_val FROM tbl_name WHERE correlated_condition) ~~~ 否则,这些子查询效率不高,可能速度会慢。把查询作为联合进行改写可能会改进效率。 相关联的子查询不能从外部查询中引用总计函数的结果。 #### 13.2.8.8. FROM子句中的子查询`` 在SELECT语句的FROM子句中,子查询是合法的。实际的语法是: ~~~ SELECT ... FROM (subquery) [AS] name ... ~~~ [AS]* name*子句是强制性的,因为FROM子句中的每个表必须有一个名称。在子查询选择列表中的任何列都必须有唯一的名称。您可以在本手册中的其它地方找到对本语法的说明。在该处,所用的词语是“导出表”。 为了进行详细说明,假设您有如下一个表: ~~~ CREATE TABLE t1 (s1 INT, s2 CHAR(5), s3 FLOAT); ~~~ 下面使用了示例表,解释了在FROM子句中如何使用子查询: ~~~ INSERT INTO t1 VALUES (1,'1',1.0); ~~~ ~~~ INSERT INTO t1 VALUES (2,'2',2.0); ~~~ ~~~ SELECT sb1,sb2,sb3 ~~~ ~~~ FROM (SELECT s1 AS sb1, s2 AS sb2, s3*2 AS sb3 FROM t1) AS sb ~~~ ~~~ WHERE sb1 > 1; ~~~ 结果:2, '2', 4.0。 下面是另一个例子:假设您想了解一个分类后的表的一组和的平均值。采用如下操作: ~~~ SELECT AVG(SUM(column1)) FROM t1 GROUP BY column1; ~~~ 不过,本查询提供所需的信息: ~~~ SELECT AVG(sum_column1) ~~~ ~~~ FROM (SELECT SUM(column1) AS sum_column1 ~~~ ~~~ FROM t1 GROUP BY column1) AS t1; ~~~ 注意,在子查询中使用的列名称(sum_column1)被整理到外部查询中。 FROM子句中的子查询可以返回标量、列、行或表。FROM子句中的子查询不能为有关联的子查询。 即使对EXPLAIN语句(即建立临时导出表),FROM子句中的子查询也会被执行。这是因为在优化过程中,上一级的查询需要有关所有表的信息。 #### 13.2.8.9. 子查询错误 以下错误只适用于子查询。本节把这些错误归在一起。 ·         来自子查询的列的数目不正确 ~~~ ·                ERROR 1241 (ER_OPERAND_COL) ~~~ ~~~ ·                SQLSTATE = 21000 ~~~ ~~~ ·                Message = "Operand should contain 1 column(s)" ~~~ 在出现以下情况时,发生此错误: ~~~ SELECT (SELECT column1, column2 FROM t2) FROM t1; ~~~ 如果您的目的是进行比较,您可以使用能返回多个列的子查询。请参见[13.2.8.5节,“行子查询”](# "13.2.8.5. Row Subqueries")。不过,在其它的语境下,子查询必须为标量操作数。 ·         来自子查询的行的数目不正确: ~~~ ·                ERROR 1242 (ER_SUBSELECT_NO_1_ROW) ~~~ ~~~ ·                SQLSTATE = 21000 ~~~ ~~~ ·                Message = "Subquery returns more than 1 row" ~~~ 如果在语句中,子查询返回的行多于一个,则发生此错误。请考虑以下例子: ~~~ SELECT * FROM t1 WHERE column1 = (SELECT column1 FROM t2); ~~~ 如果SELECT column1 FROM t2只返回一行,则将执行以前的查询。如果子查询返回的行多于一个,则将出现错误1242。在这种情况下,该查询将被改写为: ~~~ SELECT * FROM t1 WHERE column1 = ANY (SELECT column1 FROM t2); ~~~ ·         在子查询中表格使用不正确: ~~~ ·                Error 1093 (ER_UPDATE_TABLE_USED) ~~~ ~~~ ·                SQLSTATE = HY000 ~~~ ~~~ ·                Message = "You can't specify target table 'x' ~~~ ~~~ ·                for update in FROM clause" ~~~ 在如下情况下,发生该错误: ~~~ UPDATE t1 SET column2 = (SELECT MAX(column1) FROM t1); ~~~ 和SELECT语句一样,在UPDATE和DELETE语句中,子查询是合法的。所以您可以在UPDATE语句中使用子查询进行赋值。不过,您不能把同一个表(在本例中为表t1)既用于子查询的FROM子句,又用于更新目标。 对于事务存储引擎,子查询的错误会导致整个语句失效。对于非事务存储引擎,在遇到错误之前进行的数据修订会被保留。 #### 13.2.8.10. 优化子查询 开发过程不断进展,所以从长远来看,没有一个可靠的优化技巧。有些技巧您可能会感兴趣,并原意采用: ·         有些子句会影响在子查询中的行的数量和顺序。使用这类子句。例如: ~~~ ·                SELECT * FROM t1 WHERE t1.column1 IN ~~~ ~~~ ·                (SELECT column1 FROM t2 ORDER BY column1); ~~~ ~~~ ·                SELECT * FROM t1 WHERE t1.column1 IN ~~~ ~~~ ·                (SELECT DISTINCT column1 FROM t2); ~~~ ~~~ ·                SELECT * FROM t1 WHERE EXISTS ~~~ ~~~ ·                (SELECT * FROM t2 LIMIT 1); ~~~ ·         用子查询替换联合。例如,试进行如下操作: ~~~ ·                SELECT DISTINCT column1 FROM t1 WHERE t1.column1 IN ( ~~~ ~~~ ·                SELECT column1 FROM t2); ~~~ 代替如下操作: ~~~ SELECT DISTINCT t1.column1 FROM t1, t2 ~~~ ~~~ WHERE t1.column1 = t2.column1; ~~~ ·         部分子查询可以被转换为联合,以便与不支持子查询的旧版本的MySQL相兼容。不过,在有些情况下,把子查询转化为联合可以提高效果。请参见[13.2.8.11节,“把子查询作为用于早期MySQL版本的联合进行改写”](# "13.2.8.11. Rewriting Subqueries as Joins for Earlier MySQL Versions")。 ·         把子句从子查询的外部转移到内部。例如,使用此查询: ~~~ ·                SELECT * FROM t1 ~~~ ~~~ ·                WHERE s1 IN (SELECT s1 FROM t1 UNION ALL SELECT s1 FROM t2); ~~~ 代替此查询: ~~~ SELECT * FROM t1 ~~~ ~~~ WHERE s1 IN (SELECT s1 FROM t1) OR s1 IN (SELECT s1 FROM t2); ~~~ 另一个例子是,使用此查询: ~~~ SELECT (SELECT column1 + 5 FROM t1) FROM t2; ~~~ 代替此查询: ~~~ SELECT (SELECT column1 FROM t1) + 5 FROM t2; ~~~ ·         使用行子查询,代替关联子查询。举例说明,使用此查询: ~~~ ·                SELECT * FROM t1 ~~~ ~~~ ·                WHERE (column1,column2) IN (SELECT column1,column2 FROM t2); ~~~ 代替此查询: ~~~ SELECT * FROM t1 ~~~ ~~~ WHERE EXISTS (SELECT * FROM t2 WHERE t2.column1=t1.column1 ~~~ ~~~ AND t2.column2=t1.column2); ~~~ ·         Use NOT (a = ANY (...)) rather than a <> ALL (...). ·         Use x = ANY (table containing (1,2)) rather than x=1 OR x=2. ·         Use = ANY rather than EXISTS. ·         对于只返回一行的无关联子查询,IN的速度慢于=。举例说明,使用此查询: ~~~ ·                SELECT * FROM t1 WHERE t1.col_name ~~~ ~~~ ·                = (SELECT a FROM t2 WHERE b = some_const); ~~~ 代替此查询: ~~~ SELECT * FROM t1 WHERE t1.col_name ~~~ ~~~ IN (SELECT a FROM t2 WHERE b = some_const); ~~~ 使用这些技巧可以使程序更快或更慢。使用BENCHMARK()函数等MySQL工具,您可以了解到在您所处的情况下,哪些技巧会有帮助。 MySQL本身进行的部分优化包括: ·         MySQL只执行一次无关联子查询。使用EXPLAIN确认给定的子查询确实是无关联的。 ·         MySQL改写IN, ALL, ANY和SOME子查询,目的是如果子查询中的select-list列已编制索引,则能发挥出此优势。 ·         MySQL使用index-lookup函数代替以下格式的子查询。EXPLAIN把此函数描述为特殊的联合类型(unique_subquery或index_subquery): ~~~ ·                ... IN (SELECT indexed_column FROM single_table ...) ~~~ ·         当表达式中不包含NULL值或空集时,MySQL使用一个包含MIN()或MAX()的表达式,对以下格式的表达式进行扩展: ~~~ ·                value {ALL|ANY|SOME} {> | < | >= | <=} (non-correlated subquery) ~~~ 例如,本WHERE子句: ~~~ WHERE 5 > ALL (SELECT x FROM t) ~~~ 可以用优化符进行如下处理: ~~~ WHERE 5 > (SELECT MAX(x) FROM t) ~~~ 在MySQL内部手册中有一章名为“MySQL如何转换子查询”,可以从[http://dev.mysql.com/doc/](http://dev.mysql.com/doc/)获取。 #### 13.2.8.11. 把子查询作为用于早期MySQL版本的联合进行改写 在较早版本的MySQL中(早于MySQL 4.1),只支持INSERT...SELECT和REPLACE...SELECT...格式的带嵌套的查询。虽然在MySQL 5.1中没有这种情况,但有时,仍然有其它的方法测试一组值的从属关系。并且,在有些情况下,不仅可以在没有子查询时对查询进行改写,而且有时使用这些方法比使用子查询效率更高。这些方法之一是IN()结构: 举例说明,本查询: ~~~ SELECT * FROM t1 WHERE id IN (SELECT id FROM t2); ~~~ 可以被改写为: ~~~ SELECT DISTINCT t1.* FROM t1, t2 WHERE t1.id=t2.id; ~~~ 以下查询: ~~~ SELECT * FROM t1 WHERE id NOT IN (SELECT id FROM t2); ~~~ ~~~ SELECT * FROM t1 WHERE NOT EXISTS (SELECT id FROM t2 WHERE t1.id=t2.id); ~~~ 也可以使用IN()进行改写: ~~~ SELECT table1.* FROM table1 LEFT JOIN table2 ON table1.id=table2.id ~~~ ~~~ WHERE table2.id IS NULL; ~~~ LEFT [OUTER] JOIN可以比对应的子查询更快,因为服务器可能对其进行更好的优化——这一点对于单独的MySQL服务器并不明确。在SQL-92之前,不存在外部联合,因此在做某些事情时,子查询是唯一的方法。现在,MySQL服务器和其它许多先进的数据库系统都能提供多种的外部联合类型。 MySQL支持multiple-table DELETE语句,该语句可以被用于高效地删除行。删除时依据来自一个表或同时来自多个表的信息。同时也支持Multiple-table UPDATE语句。 ### 13.2.9. TRUNCATE语法 ~~~ TRUNCATE [TABLE] tbl_name ~~~ TRUNCATE TABLE用于完全清空一个表。从逻辑上说,该语句与用于删除所有行的DELETE语句等同,但是在有些情况下,两者在使用上有所不同。 对于InnoDB表,如果有需要引用表的外键限制,则TRUNCATE TABLE被映射到DELETE上;否则使用快速删减(取消和重新创建表)。使用TRUNCATE TABLE重新设置AUTO_INCREMENT计数器,设置时不考虑是否有外键限制。 对于其它存储引擎,在MySQL 5.1中,TRUNCATE TABLE与DELETE FROM有以下几处不同: ·         删减操作会取消并重新创建表,这比一行一行的删除行要快很多。 ·         删减操作不能保证对事务是安全的;在进行事务处理和表锁定的过程中尝试进行删减,会发生错误。 ·         被删除的行的数目没有被返回。 ·         只要表定义文件*tbl_name*.frm是合法的,则可以使用TRUNCATE TABLE把表重新创建为一个空表,即使数据或索引文件已经被破坏。 ·         表管理程序不记得最后被使用的AUTO_INCREMENT值,但是会从头开始计数。即使对于MyISAM和InnoDB也是如此。MyISAM和InnoDB通常不再次使用序列值。 ·         当被用于带分区的表时,TRUNCATE TABLE会保留分区;即,数据和索引文件被取消并重新创建,同时分区定义(.par)文件不受影响。 TRUNCATE TABLE是在MySQL中采用的一个Oracle SQL扩展。 ### 13.2.10. UPDATE语法 Single-table语法: ~~~ UPDATE [LOW_PRIORITY] [IGNORE] tbl_name ~~~ ~~~     SET col_name1=expr1 [, col_name2=expr2 ...] ~~~ ~~~     [WHERE where_definition] ~~~ ~~~     [ORDER BY ...] ~~~ ~~~     [LIMIT row_count] ~~~ Multiple-table语法: ~~~ UPDATE [LOW_PRIORITY] [IGNORE] table_references ~~~ ~~~     SET col_name1=expr1 [, col_name2=expr2 ...] ~~~ ~~~     [WHERE where_definition] ~~~ UPDATE语法可以用新值更新原有表行中的各列。SET子句指示要修改哪些列和要给予哪些值。WHERE子句指定应更新哪些行。如果没有WHERE子句,则更新所有的行。如果指定了ORDER BY子句,则按照被指定的顺序对行进行更新。LIMIT子句用于给定一个限值,限制可以被更新的行的数目。 UPDATE语句支持以下修饰符: ·         如果您使用LOW_PRIORITY关键词,则UPDATE的执行被延迟了,直到没有其它的客户端从表中读取为止。 ·         如果您使用IGNORE关键词,则即使在更新过程中出现错误,更新语句也不会中断。如果出现了重复关键字冲突,则这些行不会被更新。如果列被更新后,新值会导致数据转化错误,则这些行被更新为最接近的合法的值。 如果您在一个表达式中通过*tbl_name*访问一列,则UPDATE使用列中的当前值。例如,以下语句把年龄列设置为比当前值多一: ~~~ mysql> UPDATE persondata SET age=age+1; ~~~ UPDATE赋值被从左到右评估。例如,以下语句对年龄列加倍,然后再进行增加: ~~~ mysql> UPDATE persondata SET age=age*2, age=age+1; ~~~ 如果您把一列设置为其当前含有的值,则MySQL会注意到这一点,但不会更新。 如果您把被已定义为NOT NULL的列更新为NULL,则该列被设置到与列类型对应的默认值,并且累加警告数。对于数字类型,默认值为0;对于字符串类型,默认值为空字符串('');对于日期和时间类型,默认值为“zero”值。 UPDATE会返回实际被改变的行的数目。Mysql_info() C API函数可以返回被匹配和被更新的行的数目,以及在UPDATE过程中产生的警告的数量。 您可以使用LIMIT * row_count*来限定UPDATE的范围。LIMIT子句是一个与行匹配的限定。只要发现可以满足WHERE子句的*row_count*行,则该语句中止,不论这些行是否被改变。 如果一个UPDATE语句包括一个ORDER BY子句,则按照由子句指定的顺序更新行。 您也可以执行包括多个表的UPDATE操作。*table_references*子句列出了在联合中包含的表。该语法在[13.2.7.1节,“JOIN语法”](# "13.2.7.1. JOIN Syntax")中进行了说明。以下是一个例子: ~~~ UPDATE items,month SET items.price=month.price ~~~ ~~~ WHERE items.id=month.id; ~~~ 以上的例子显示出了使用逗号操作符的内部联合,但是multiple-table UPDATE语句可以使用在SELECT语句中允许的任何类型的联合,比如LEFT JOIN。 注释:您不能把ORDER BY或LIMIT与multiple-table UPDATE同时使用。 在一个被更改的multiple-table UPDATE中,有些列被引用。您只需要这些列的UPDATE权限。有些列被读取了,但是没被修改。您只需要这些列的SELECT权限。 如果您使用的multiple-table UPDATE语句中包含带有外键限制的InnoDB表,则MySQL优化符处理表的顺序可能与上下层级关系的顺序不同。在此情况下,语句无效并被 回滚。同时,更新一个单一表,并且依靠ON UPDATE功能。该功能由InnoDB提供,用于对其它表进行相应的修改。请参见[15.2.6.4节,“FOREIGN KEY约束”](# "15.2.6.4. FOREIGN KEY Constraints")。 目前,您不能在一个子查询中更新一个表,同时从同一个表中选择。 ### 13.3. MySQL实用工具语句 [13.3.1. DESCRIBE语法(获取有关列的信息)](#)[13.3.2. USE语法](#) ### 13.3.1. DESCRIBE语法(获取有关列的信息) ~~~ {DESCRIBE | DESC} tbl_name [col_name | wild] ~~~ DESCRIBE可以提供有关表中各列的信息。它是SHOW COLUMNS FROM的快捷方式。这些语句也可以显示语句,用于阅览。 见[13.5.4.3节,“SHOW COLUMNS语法”](# "13.5.4.3. SHOW COLUMNS Syntax")。 *col_name*可以是一个列名称,或一个包含‘%’和‘_’的通配符的字符串,用于获得对于带有与字符串相匹配的名称的各列的输出。没有必要在引号中包含字符串,除非其中包含空格或其它特殊字符。 ~~~ mysql> DESCRIBE city; ~~~ ~~~ +------------+----------+------+-----+---------+----------------+ ~~~ ~~~ | Field      | Type     | Null | Key | Default | Extra          | ~~~ ~~~ +------------+----------+------+-----+---------+----------------+ ~~~ ~~~ | Id         | int(11)  | NO   | PRI | NULL    | auto_increment | ~~~ ~~~ | Name       | char(35) | NO   |     |         |                | ~~~ ~~~ | Country    | char(3)  | NO   | UNI |         |                | ~~~ ~~~ | District   | char(20) | YES  | MUL |         |                | ~~~ ~~~ | Population | int(11)  | NO   |     | 0       |                | ~~~ ~~~ +------------+----------+------+-----+---------+----------------+ ~~~ ~~~ 5 rows in set (0.00 sec) ~~~ NULL字段指示是否NULL可以被存储在列中。 Key字段指示是否该列已编制索引。PRI的值指示该列是表的主键的一部分。UNI指示,该列是UNIQUE索引的一部分。MUL值指示,在列中某个给定值多次出现是允许的。 MUL将被显示在UNIQUE索引中,原因之一是多个列会组合成一个复合UNIQUE索引;尽管列的组合是唯一的,但每个列仍可以多次出现同一个给定值。注意,在复合索引中,只有索引最左边的列可以进入Key字段中。 默认字段指示,默认值被赋予该列。 Extra字段包含可以获取的与给定列有关的附加信息。在我们的例子中,Extra字段指示,Id列使用AUTO_INCREMENT关键词创建。 如果列类型与您预计的依据CREATE TABLE语句得出的列类型不同,则请注意,MySQL有时会改变列类型。请参见[13.1.5.1节,“沉寂的列规格变更”](# "13.1.5.1. Silent Column Specification Changes")。 DESCRIBE语句被设立出来,用于与Oracle相兼容。 SHOW CREATE TABLE和SHOW TABLE STATUS语句也可以提供有关表的信息。请参见[13.5.4节,“SHOW语法”](# "13.5.4. SHOW Syntax")。 ### 13.3.2. USE语法 ~~~ USE db_name ~~~ USE *db_name*语句可以通告MySQL把*db_name*数据库作为默认(当前)数据库使用,用于后续语句。该数据库保持为默认数据库,直到语段的结尾,或者直到发布一个不同的USE语句: ~~~ mysql> USE db1; ~~~ ~~~ mysql> SELECT COUNT(*) FROM mytable;   # selects from db1.mytable ~~~ ~~~ mysql> USE db2; ~~~ ~~~ mysql> SELECT COUNT(*) FROM mytable;   # selects from db2.mytable ~~~ 使用USE语句为一个特定的当前的数据库做标记,不会阻碍您访问其它数据库中的表。下面的例子可以从db1数据库访问作者表,并从db2数据库访问编辑表: ~~~ mysql> USE db1; ~~~ ~~~ mysql> SELECT author_name,editor_name FROM author,db2.editor ~~~ ~~~     ->        WHERE author.editor_id = db2.editor.editor_id; ~~~ USE语句被设立出来,用于与Sybase相兼容。 ### 13.4. MySQL事务处理和锁定语句 [13.4.1. START TRANSACTION, COMMIT和ROLLBACK语法](#)[ 13.4.2. 不能回滚的语句](#)[ 13.4.3. 会造成隐式提交的语句](#)[13.4.4. SAVEPOINT和ROLLBACK TO SAVEPOINT语法](#)[13.4.5. LOCK TABLES和UNLOCK TABLES语法](#)[13.4.6. SET TRANSACTION语法](#)[ 13.4.7. XA事务](#) MySQL通过SET AUTOCOMMIT, START TRANSACTION, COMMIT和ROLLBACK等语句支持本地事务(在给定的客户端连接中)。请参见[13.4.1节,“START TRANSACTION, COMMIT和ROLLBACK语法”](# "13.4.1. START TRANSACTION, COMMIT, and ROLLBACK Syntax")。XA事务支持还可以允许MySQL参与分布式事务。请参见[13.4.7节,“XA事务”](# "13.4.7. XA Transactions")。 ### 13.4.1. START TRANSACTION, COMMIT和ROLLBACK语法 ~~~ START TRANSACTION | BEGIN [WORK] ~~~ ~~~ COMMIT [WORK] [AND [NO] CHAIN] [[NO] RELEASE] ~~~ ~~~ ROLLBACK [WORK] [AND [NO] CHAIN] [[NO] RELEASE] ~~~ ~~~ SET AUTOCOMMIT = {0 | 1} ~~~ START TRANSACTION或BEGIN语句可以开始一项新的事务。COMMIT可以提交当前事务,是变更成为永久变更。ROLLBACK可以 回滚当前事务,取消其变更。SET AUTOCOMMIT语句可以禁用或启用默认的autocommit模式,用于当前连接。 自选的WORK关键词被支持,用于COMMIT和RELEASE,与CHAIN和RELEASE子句。CHAIN和RELEASE可以被用于对事务完成进行附加控制。Completion_type系统变量的值决定了默认完成的性质。请参见[5.3.3节,“服务器系统变量”](# "5.3.3. Server System Variables")。 AND CHAIN子句会在当前事务结束时,立刻启动一个新事务,并且新事务与刚结束的事务有相同的隔离等级。RELEASE子句在终止了当前事务后,会让服务器断开与当前客户端的连接。包含NO关键词可以抑制CHAIN或RELEASE完成。如果completion_type系统变量被设置为一定的值,使连锁或释放完成可以默认进行,此时NO关键词有用。 默认情况下,MySQL采用autocommit模式运行。这意味着,当您执行一个用于更新(修改)表的语句之后,MySQL立刻把更新存储到磁盘中。 如果您正在使用一个事务安全型的存储引擎(如InnoDB, BDB或NDB簇),则您可以使用以下语句禁用autocommit模式: ~~~ SET AUTOCOMMIT=0; ~~~ 通过把AUTOCOMMIT变量设置为零,禁用autocommit模式之后,您必须使用COMMIT把变更存储到磁盘中,或着如果您想要忽略从事务开始进行以来做出的变更,使用ROLLBACK。 如果您想要对于一个单一系列的语句禁用autocommit模式,则您可以使用START TRANSACTION语句: ~~~ START TRANSACTION; ~~~ ~~~ SELECT @A:=SUM(salary) FROM table1 WHERE type=1; ~~~ ~~~ UPDATE table2 SET summary=@A WHERE type=1; ~~~ ~~~ COMMIT; ~~~ 使用START TRANSACTION,autocommit仍然被禁用,直到您使用COMMIT或ROLLBACK结束事务为止。然后autocommit模式恢复到原来的状态。 BEGIN和BEGIN WORK被作为START TRANSACTION的别名受到支持,用于对事务进行初始化。START TRANSACTION是标准的SQL语法,并且是启动一个ad-hoc事务的推荐方法。BEGIN语句与BEGIN关键词的使用不同。BEGIN关键词可以启动一个BEGIN...END复合语句。后者不会开始一项事务。请参见[20.2.7节,“BEGIN ... END复合语句”](# "20.2.7. BEGIN ... END Compound Statement")。 您也可以按照如下方法开始一项事务: ~~~ START TRANSACTION WITH CONSISTENT SNAPSHOT; ~~~ WITH CONSISTENT SNAPSHOT子句用于启动一个一致的读取,用于具有此类功能的存储引擎。目前,该子句只适用于InnoDB。该子句的效果与发布一个START TRANSACTION,后面跟一个来自任何InnoDB表的SELECT的效果一样。请参见[15.2.10.4节,“一致的非锁定读”](# "15.2.10.4. Consistent Non-Locking Read")。 开始一项事务会造成一个隐含的UNLOCK TABLES被执行。 为了获得最好的结果,事务应只使用由单一事务存储引擎管理的表执行。否则,会出现以下问题: ·         如果您使用的表来自多个事务安全型存储引擎(例如InnoDB和BDB),并且事务隔离等级不是SERIALIZABLE,则有可能当一个事务提交时,其它正在进行中的、使用同样的表的事务将只会发生由第一个事务产生的变更。也就是,用混合引擎不能保证事务的原子性,并会造成不一致。(如果混合引擎事务不经常有,则您可以根据需要使用SET TRANSACTION ISOLATION LEVEL把隔离等级设置到SERIALIZABLE。) ·         如果您在事务中使用非事务安全型表,则对这些表的任何变更被立刻存储,不论autocommit模式的状态如何。 如果您在更新了事务中一个事务表之后,发布一个ROLLBACK语句,则会出现一个ER_WARNING_NOT_COMPLETE_ROLLBACK警告。对事务安全型表的变更被 回滚,但是对非事务安全型表没有变更。 每个事务被存储在一个组块中的二进制日志中,在COMMIT之上。被回滚的事务不被计入日志。(例外情况:对非事务表的更改不会被 回滚。如果一个被回滚的事务包括对非事务表的更改,则整个事务使用一个在末端的ROLLBACK语句计入日志,以确保对这些表的更改进行复制。)见[5.11.3节,“二进制日志”](# "5.11.3. The Binary Log")。 您可以使用SET TRANSACTION ISOLATION LEVEL更改事务的隔离等级。请参见[13.4.6节,“SET TRANSACTION语法”](# "13.4.6. SET TRANSACTION Syntax")。 回滚可以慢速运行。在用户没有明确要求时,也可以进行回滚(例如,当错误发生时)。因此,在明确地和隐含的(ROLLBACK SQL命令)回滚时,SHOW PROCESSLIST会在Stage列中显示Rolling back,用于连接。 ### 13.4.2. 不能回滚的语句 有些语句不能被回滚。通常,这些语句包括数据定义语言(DDL)语句,比如创建或取消数据库的语句,和创建、取消或更改表或存储的子程序的语句。 您在设计事务时,不应包含这类语句。如果您在事务的前部中发布了一个不能被回滚的语句,则后部的其它语句会发生错误,在这些情况下,通过发布ROLLBACK语句不能 回滚事务的全部效果。 ### 13.4.3. 会造成隐式提交的语句 以下语句(以及同义词)均隐含地结束一个事务,似乎是在执行本语句前,您已经进行了一个COMMIT。 ·         ALTER FUNCTION, ALTER PROCEDURE, ALTER TABLE, BEGIN, CREATE DATABASE, CREATE FUNCTION, CREATE INDEX, CREATE PROCEDURE, CREATE TABLE, DROP DATABASE, DROP FUNCTION, DROP INDEX, DROP PROCEDURE, DROP TABLE, LOAD MASTER DATA, LOCK TABLES, RENAME TABLE, SET AUTOCOMMIT=1, START TRANSACTION, TRUNCATE TABLE, UNLOCK TABLES. ·         当当前所有的表均被锁定时,UNLOCK TABLES可以提交事务。 ·         CREATE TABLE, CREATE DATABASEDROP DATABASE, TRUNCATE TABLE, ALTER FUNCTION, ALTER PROCEDURE, CREATE FUNCTION, CREATE PROCEDURE, DROP FUNCTION和DROP PROCEDURE等语句会导致一个隐含提交。 ·         InnoDB中的CREATE TABLE语句被作为一个单一事务进行处理。这意味着,来自用户的ROLLBACK不会撤销用户在事务处理过程中创建的CREATE TABLE语句。 事务不能被嵌套。这是隐含COMMIT的结果。当您发布一个START TRANSACTION语句或其同义词时,该COMMIT被执行,用于任何当前事务。 ### 13.4.4. SAVEPOINT和ROLLBACK TO SAVEPOINT语法 ~~~ SAVEPOINT identifier ~~~ ~~~ ROLLBACK [WORK] TO SAVEPOINT identifier ~~~ ~~~ RELEASE SAVEPOINT identifier ~~~ InnoDB支持SQL语句SAVEPOINT, ROLLBACK TO SAVEPOINT, RELEASE SAVEPOINT和自选的用于ROLLBACK的WORK关键词。 SAVEPOINT语句用于设置一个事务保存点,带一个标识符名称。如果当前事务有一个同样名称的保存点,则旧的保存点被删除,新的保存点被设置。 ROLLBACK TO SAVEPOINT语句会向以命名的保存点回滚一个事务。如果在保存点被设置后,当前事务对行进行了更改,则这些更改会在 回滚中被撤销。但是,InnoDB不会释放被存储在保存点之后的存储器中的行锁定。(注意,对于新插入的行,锁定信息被存储在行中的事务ID承载;锁定没有被分开存储在存储器中。在这种情况下,行锁定在撤销中被释放。)在被命名的保存点之后设置的保存点被删除。 如果语句返回以下错误,则意味着不存在带有指定名称的保存点: ~~~ ERROR 1181: Got error 153 during ROLLBACK ~~~ RELEASE SAVEPOINT语句会从当前事务的一组保存点中删除已命名的保存点。不出现提交或 回滚。如果保存点不存在,会出现错误。 如果您执行COMMIT或执行不能命名保存点的ROLLBACK,则当前事务的所有保存点被删除。 ### 13.4.5. LOCK TABLES和UNLOCK TABLES语法 ~~~ LOCK TABLES ~~~ ~~~     tbl_name [AS alias] {READ [LOCAL] | [LOW_PRIORITY] WRITE} ~~~ ~~~     [, tbl_name [AS alias] {READ [LOCAL] | [LOW_PRIORITY] WRITE}] ... ~~~ ~~~ UNLOCK TABLES ~~~ LOCK TABLES可以锁定用于当前线程的表。如果表被其它线程锁定,则造成堵塞,直到可以获取所有锁定为止。UNLOCK TABLES可以释放被当前线程保持的任何锁定。当线程发布另一个LOCK TABLES时,或当与服务器的连接被关闭时,所有由当前线程锁定的表被隐含地解锁。   表锁定只用于防止其它客户端进行不正当地读取和写入。保持锁定(即使是读取锁定)的客户端可以进行表层级的操作,比如DROP TABLE。 注意,下面是对事务表使用LOCK TABLES的说明: ·         在尝试锁定表之前,LOCK TABLES不是事务安全型的,会隐含地提交所有活性事务。同时,开始一项事务(例如,使用START TRANSACTION),会隐含地执行UNLOCK TABLES。(见[13.4.3节,“会造成隐式提交的语句”](# "13.4.3. Statements That Cause an Implicit Commit")。 ·         对事务表(如InnoDB)使用LOCK TABLES的正确方法是,设置AUTOCOMMIT=0并且不能调用UNLOCK TABLES,直到您明确地提交事务为止。当您调用LOCK TABLES时,InnoDB会内部地取其自己的表锁定,MySQL取其自己的表锁定。InnoDB在下一个提交时释放其表锁定,但是,对于MySQL,要释放表锁定,您必须调用UNLOCK TABLES。您不应该让AUTOCOMMIT=1,因为那样的话,InnoDB会在调用LOCK TABLES之后立刻释放表锁定,并且很容易形成死锁定。注意,如果AUTOCOMMIT=1,我们根本不能获取InnoDB表锁定,这样就可以帮助旧的应用软件避免不必要的死锁定。 ·         ROLLBACK不会释放MySQL的非事务表锁定。 要使用LOCK TABLES,您必须拥有相关表的LOCK TABLES权限和SELECT权限。 使用LOCK TABLES的主要原因是仿效事务,或在更新表时加快速度。这将在后面进行更详细的解释。 如果一个线程获得对一个表地READ锁定,该线程(和所有其它线程)只能从该表中读取。如果一个线程获得对一个表的WRITE锁定,只有保持锁定的线程可以对表进行写入。其它的线程被阻止,直到锁定被释放时为止。 READ LOCAL和READ之间的区别是,READ LOCAL允许在锁定被保持时,执行非冲突性INSERT语句(同时插入)。但是,如果您正打算在MySQL外面操作数据库文件,同时您保持锁定,则不能使用READ LOCAL。对于InnoDB表,READ LOCAL与READ相同。 当您使用LOCK TABLES时,您必须锁定您打算在查询中使用的所有的表。虽然使用LOCK TABLES语句获得的锁定仍然有效,但是您不能访问没有被此语句锁定的任何的表。同时,您不能在一次查询中多次使用一个已锁定的表——使用别名代替,在此情况下,您必须分别获得对每个别名的锁定。 ~~~ mysql> LOCK TABLE t WRITE, t AS t1 WRITE; ~~~ ~~~ mysql> INSERT INTO t SELECT * FROM t; ~~~ ~~~ ERROR 1100: Table 't' was not locked with LOCK TABLES ~~~ ~~~ mysql> INSERT INTO t SELECT * FROM t AS t1; ~~~ 如果您的查询使用一个别名引用一个表,那么您必须使用同样的别名锁定该表。如果没有指定别名,则不会锁定该表。 ~~~ mysql> LOCK TABLE t READ; ~~~ ~~~ mysql> SELECT * FROM t AS myalias; ~~~ ~~~ ERROR 1100: Table 'myalias' was not locked with LOCK TABLES ~~~ 相反的,如果您使用一个别名锁定一个表,您必须使用该别名在您的查询中引用该表。 ~~~ mysql> LOCK TABLE t AS myalias READ; ~~~ ~~~ mysql> SELECT * FROM t; ~~~ ~~~ ERROR 1100: Table 't' was not locked with LOCK TABLES ~~~ ~~~ mysql> SELECT * FROM t AS myalias; ~~~ WRITE锁定通常比READ锁定拥有更高的优先权,以确保更新被尽快地处理。这意味着,如果一个线程获得了一个READ锁定,则另一个线程会申请一个WRITE锁定,后续的READ锁定申请会等待,直到WRITE线程获得锁定并释放锁定。您可以使用LOW_PRIORITY WRITE锁定来允许其它线程在该线程正在等待WRITE锁定时获得READ锁定。只有当您确定最终将有一个时机,此时没有线程拥有READ锁定时,您才应该使用LOW_PRIORITY WRITE锁定。 LOCK TABLES按照如下方式执行: 1.    按照内部定义的顺序,对所有要被锁定的表进行分类。从用户的角度,此顺序是未经定义的。 2.    如果使用一个读取和一个写入锁定对一个表进行锁定,则把写入锁定放在读取锁定之前。 3.    一次锁定一个表,直到线程得到所有锁定为止。 该规则确保表锁定不会出现死锁定。但是,对于该规则,您需要注意其它的事情: 如果您正在对一个表使用一个LOW_PRIORITY WRITE锁定,这只意味着,MySQL等待特定的锁定,直到没有申请READ锁定的线程时为止。当线程已经获得WRITE锁定,并正在等待得到锁定表清单中的用于下一个表的锁定时,所有其它线程会等待WRITE锁定被释放。如果这成为对于应用程序的严重的问题,则您应该考虑把部分表转化为事务安全型表。 您可以安全地使用KILL来结束一个正在等待表锁定的线程。请参见[13.5.5.3节,“KILL语法”](# "13.5.5.3. KILL Syntax")。 注意,您不能使用INSERT DELAYED锁定任何您正在使用的表,因为,在这种情况下,INSERT由另一个线程执行。 通常,您不需要锁定表,因为所有的单个UPDATE语句都是原子性的;没有其它的线程可以干扰任何其它当前正在执行的SQL语句。但是,在几种情况下,锁定表会有好处: ·         如果您正在对一组MyISAM表运行许多操作,锁定您正在使用的表,可以快很多。锁定MyISAM表可以加快插入、更新或删除的速度。不利方面是,没有线程可以更新一个用READ锁定的表(包括保持锁定的表),也没有线程可以访问用WRITE锁定的表(除了保持锁定的表以外)。 有些MyISAM操作在LOCK TABLES之下更快的原因是,MySQL不会清空用于已锁定表的关键缓存,直到UNLOCK TABLE被调用为止。通常,关键缓存在每个SQL语句之后被清空。 ·         如果您正在使用MySQL中的一个不支持事务的存储引擎,则如果您想要确定在SELECT和UPDATE之间没有其它线程,您必须使用LOCK TABLES。本处所示的例子要求LOCK TABLES,以便安全地执行: ~~~ ·                mysql> LOCK TABLES trans READ, customer WRITE; ~~~ ~~~ ·                mysql> SELECT SUM(value) FROM trans WHERE customer_id=some_id; ~~~ ~~~ ·                mysql> UPDATE customer ~~~ ~~~ ·                    ->     SET total_value=sum_from_previous_statement ~~~ ~~~ ·                    ->     WHERE customer_id=some_id; ~~~ ~~~ ·                mysql> UNLOCK TABLES; ~~~ 如果没有LOCK TABLES,有可能另一个线程会在执行SELECT和UPDATE语句之间在trans表中插入一个新行。 通过使用相对更新(UPDATE customer SET *value*=*value*+*new_value*)或LAST_INSERT_ID()函数,您可以在许多情况下避免使用LOCK TABLES。请参见[1.8.5.3节,“事务和原子操作”](# "1.8.5.3. Transactions and Atomic Operations")。 通过使用用户层级的顾问式锁定函数GET_LOCK()和RELEASE_LOCK(),您也可以在有些情况下避免锁定表。这些锁定被保存在服务器中的一个混编表中,使用pthread_mutex_lock()和pthread_mutex_unlock(),以加快速度。请参见[12.9.4节,“其他函数”](# "12.9.4. Miscellaneous Functions")。 要了解更多有关锁定规则的说明,请参见[7.3.1节,“锁定方法”](# "7.3.1. Locking Methods")。 您可以使用FLUSH TABLES WITH READ LOCK语句锁定位于所有带有读取锁定的数据库中的所有表。请参见[13.5.5.2节,“FLUSH语法”](# "13.5.5.2. FLUSH Syntax")。如果您有一个可以及时拍摄快照的文件系统,比如Veritas,这是获得备份的一个非常方便的方式。 注释:如果您对一个已锁定的表使用ALTER TABLE,该表可能会解锁。请参见[A.7.1节,“与ALTER TABLE有关的问题”](# "A.7.1. Problems with ALTER TABLE")。 ### 13.4.6. SET TRANSACTION语法 ~~~ SET [GLOBAL | SESSION] TRANSACTION ISOLATION LEVEL ~~~ ~~~ { READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SERIALIZABLE } ~~~ 本语句用于设置事务隔离等级,用于下一个事务,或者用于当前会话。 在默认情况下,SET TRANSACTION会为下一个事务(还未开始)设置隔离等级。如果您使用GLOBAL关键词,则语句会设置全局性的默认事务等级,用于从该点以后创建的所有新连接。原有的连接不受影响。要进行此操作,您需要SUPER权限。使用SESSION关键测可以设置默认事务等级,用于对当前连接执行的所有将来事务。 要了解对每个InnoDB事务隔离等级的描述,请参见[15.2.10.3节,“InnoDB和TRANSACTION ISOLATION LEVEL”](# "15.2.10.3. InnoDB and TRANSACTION ISOLATION LEVEL")。InnoDB支持MySQL 5.1中的各个等级。默认的等级是REPEATABLE READ。 您可以使用--transaction-isolation选项,对**mysqld**设置初始的默认全局隔离等级。请参见[5.3.1节,“**mysqld**命令行选项”](# "5.3.1. mysqld Command-Line Options")。 ### 13.4.7. XA事务 [ 13.4.7.1. XA事务SQL语法](#)[13.4.7.2. XA事务状态](#) 对于InnoDB存储引擎,可以获得对XA事务的支持。MySQL XA的执行依据X/Open CAE文件*Distributed Transaction Processing: The XA Specification*。本文件由Open Group出版,可以从[http://www.opengroup.org/public/pubs/catalog/c193.htm](http://www.opengroup.org/public/pubs/catalog/c193.htm)获取。在[I.5节,“对XA事务的限制”](# "I.5. Restrictions on XA Transactions")对当前XA执行的限制进行了描述。 在客户端方面,没有特殊要求。连接MySQL服务器的XA接口由以XA关键词开头的SQL语句组成。MySQL客户端必须能发送SQL语句,并能理解XA语句接口的语义,但是它们不需要被链接到特定的MySQL客户库上。 当前,在MySQL连接器当中,MySQL连接器/J 5.0.0直接支持XA(也就是,通过一个可以控制XA SQL语句接口的等级接口)。 XA支持分布式的事务,具备能力,让多个独立的事务资源参加全局的事务。事务资源通常是RDBMSs,不过也可以是其它种类的资源。 一个全局事务会涉及到多个行动,这些行动本身是事务性的。不过,所有行动都必须作为一个群组成功完成,或者作为一个群组被回滚。实际上,这会延伸ACID性质,“提高等级”,这样,多个ACID事务就可以一起执行,相当于也拥有ACID性质的全局操作的组件。(但是,对于一个分布式事务,您必须使用SERAILIZABLE隔离等级,以实现ACID性质。对于一个非分布式事务,使用REPEATABLE READ就足够了。但是对于分布式事务,使用REPEATABLE READ是不够的。) 分布式事务的部分示例: ·         应用程序相当于一个把消息传递服务和RDBMS组合在一起的整合工具。应用程序可以确保,所有进行消息发送、回收和处理的事务(同时包含一个事务数据库)均在一个全局事务中发生。您可以把这看作是“事务电子邮件。” ·         应用程序执行的行动会涉及到不同数据库服务器,比如MySQL服务器和Oracle服务器(或多个MySQL服务器)。涉及到多个服务器的行动必须作为一个全局事务的一部分发生,而不是作为针对每个服务器的独立的本地事务发生。 ·         银行把帐户信息保存在RDBMS中,并通过自动取款机(ATMs)分发和收取欠款。必须要确保ATM行动被正确地反映到帐户中,但是这不能只由RDBMS单独完成。全局事务管理器会整合ATM和数据库资源,以确保财务事务的整体一致性。 使用全局事务的应用程序涉及一个或多个资源管理器和一个事务管理器: ·         资源管理器(RM)用于提供通向事务资源的途径。数据库服务器是一种资源管理器。该管理器必须可以提交或 回滚由RM管理的事务。 ·         事务管理器(TM)用于协调作为一个全局事务一部分的事务。TM与管理每个事务的RMs进行通讯。一个全局事务中各个单个事务均是全局事务的“分支”。全局事务和各分支通过一种命名方法进行标识。这种命名方法在后面进行讲述。 MySQL执行XA MySQL时,MySQL服务器相当于一个用于管理全局事务中的XA事务的资源管理器。与MySQL服务器连接的客户端相当于事务管理器。 要执行一个全局事务,必须知道涉及到了哪些组件,并且把每个组件引到一点,在此时,组件可以被提交或回滚时。根据每个组件报告的有关组件效能的内容,这些组件必须作为一个原子性群组全部提交或 回滚。即,要么所有的组件必须提交,要么所有的组件必须回滚。要管理一个全局事务,必须要考虑任何组件或连接网络可能会故障。 用于执行全局事务的过程使用两阶段提交(2PC),发生时间在由全局事务的分支进行的行动已经被执行之后。 1.    在第一阶段,所有的分支被预备好。即,它们被TM告知要准备提交。通常,这意味着用于管理分支的每个RM会记录对于被稳定保存的分支的行动。分支指示是否它们可以这么做。这些结果被用于第二阶段。 2.    在第二阶段,TM告知RMs是否要提交或 回滚。如果在预备分支时,所有的分支指示它们将能够提交,则所有的分支被告知要提交。如果在预备时,有任何分支指示它将不能提交,则所有分支被告知 回滚。 在有些情况下,一个全局事务可能会使用一阶段提交(1PC)。例如,当一个事务管理器发现,一个全局事务只由一个事务资源组成(即,单一分支),则该资源可以被告知同时进行预备和提交。 #### 13.4.7.1. XA事务SQL语法 要在MySQL中执行XA事务,应使用以下语句: ~~~ XA {START|BEGIN} xid [JOIN|RESUME] ~~~ ~~~   ~~~ ~~~ XA END xid [SUSPEND [FOR MIGRATE]] ~~~ ~~~   ~~~ ~~~ XA PREPARE xid ~~~ ~~~   ~~~ ~~~ XA COMMIT xid [ONE PHASE] ~~~ ~~~   ~~~ ~~~ XA ROLLBACK xid ~~~ ~~~   ~~~ ~~~ XA RECOVER ~~~ 对于XA START,JOIN和RESUME子句不被支持。 对于XA END,SUSPEND [FOR MIGRATE]子句不被支持。 每个XA语句以XA关键词为开头,多数语句要求一个xid值。 xid是一个XA事务标识符。它指示该语句适用于哪个事务。xid值由客户端提供,或由MySQL服务器生成。xid值包含一到三个部分: ~~~ xid: gtrid [, bqual [, formatID ]] ~~~ gtrid是一个全局事务标识符,bqual是一个分支限定符,formatID是一个数字,用于标识由gtrid和bqual值使用的格式。根据语法的表示,bqual和formatID是自选的。如果没有给定,默认的bqual值是''。如果没有给定,默认的fromatID值是1。 gtrid和bqual必须为字符串文字,每个的长度最多为64字节(不是字符)。gtrid和bqual可以用多种方法指定。您可以使用带引号的字符串('ab'),十六进制字符串(0x6162, X'ab'),或位值(b'*nnnn*')。 formatID是一个无符号的整数。 通过MySQL服务器的带下划线的XA支持子程序,gtrid和bqual值被理解为以字节为单位。但是,在包含XA语句的SQL语句正在被分析的同时,服务器会去操作一些特定的字符集。为了安全,把gtrid和bqual作为十六进制字符串写入。 通常,xid值由事务管理器生成。由一个TM生成的值必须与由其它TMs生成的值不同。一个给定的TM必须能识别自己的xid值。这些值位于由XA RECOVER语句返回的值清单中。 XA START xid用于启动一个带给定xid值的XA事务。每个XA事务必须有一个唯一的xid值,因此该值当前不能被其它的XA事务使用。使用gtrid和bqual值评估唯一性。所有下列的用于XA事务的XA语句必须使用与XA START语句中给定的相同的xid值进行指定。如果您使用这些语句,但是指定的xid值与部分原有的XA事务不对应的话,会发生错误。 一项或多项XA事务可以是同一个全局事务的一部分。在一个给定的全局事务中的所有XA事务必须在xid值中使用同样的gtrid值。出于这个原因,gtrid值必须为全局唯一的,这样,有关一个给定的XA事务是哪个全局事务的一部分的问题就不会含糊不清。对于一个全局事务中的每个XA事务,xid值中的bqual部分必须不一样。(bqual值应不一样,这个要求是当前执行MySQL XA的一个限制条件。这不是XA规约的一部分。) 对于在MySQL服务器上的处于PREPARED状态的XA事务,XA RECOVER语句会返回信息。(见[13.4.7.2节,“XA事务状态”](# "13.4.7.2. XA Transaction States").。)输出包括一个行,该行用于服务器上的每个这类XA事务,不论是哪个客户端启动了它。 XA RECOVER输出行看上去像这样(例如,xid值包括'abc', 'def'和 7等部分): ~~~ mysql> XA RECOVER; ~~~ ~~~ +----------+--------------+--------------+--------+ ~~~ ~~~ | formatID | gtrid_length | bqual_length | data   | ~~~ ~~~ +----------+--------------+--------------+--------+ ~~~ ~~~ |        7 |            3 |            3 | abcdef | ~~~ ~~~ +----------+--------------+--------------+--------+ ~~~ 输出列有以下意义: ·         formatID是事务xid的formatID部分 ·         gtrid_length是xid的gtrid部分的长度,以字节为单位 ·         bqual_length是xid的bqual部分的长度,以字节为单位 ·         data是xid的gtrid部分和bqual部分的串联 #### 13.4.7.2. XA事务状态 XA事务在以下状态下进展: 1.    使用XA START来启动一个XA事务,并把它放入ACTIVE状态。 2.    对于一个ACTIVE XA事务,发布构成事务的SQL语句,然后发布一个XA END语句。XA END把事务放入IDLE状态。 3.    对于一个IDLE XA事务,您可以发布一个XA PREPARE语句或一个XA COMMIT…ONE PHASE语句: ·         XA PREPARE把事务放入PREPARED状态。在此点上的XA RECOVER语句将在其输出中包括事务的xid值,因为XA RECOVER会列出处于PREPARED状态的所有XA事务。 ·         XA COMMIT…ONE PHASE用于预备和提交事务。xid值将不会被XA RECOVER列出,因为事务终止。 4.    对于一个PREPARED XA事务,您可以发布一个XA COMMIT语句来提交和终止事务,或者发布XA ROLLBACK来回滚并终止事务。 下面是一个简单的XA事务,该事务把一行作为一个全局事务的一部分插入一个表中。 ~~~ mysql> XA START 'xatest'; ~~~ ~~~ Query OK, 0 rows affected (0.00 sec) ~~~ ~~~   ~~~ ~~~ mysql> INSERT INTO mytable (i) VALUES(10); ~~~ ~~~ Query OK, 1 row affected (0.04 sec) ~~~ ~~~   ~~~ ~~~ mysql> XA END 'xatest'; ~~~ ~~~ Query OK, 0 rows affected (0.00 sec) ~~~ ~~~   ~~~ ~~~ mysql> XA PREPARE 'xatest'; ~~~ ~~~ Query OK, 0 rows affected (0.00 sec) ~~~ ~~~   ~~~ ~~~ mysql> XA COMMIT 'xatest'; ~~~ ~~~ Query OK, 0 rows affected (0.00 sec) ~~~ 根据一个给定的客户端连接的语境,XA事务和本地(非XA)事务互相排斥。举例说明,如果已经发布了XA START来开始一项XA事务,则本地事务不会被启动,直到XA事务已经被提交或被 回滚为止。相反的,如果已经使用START TRANSACTION启动一个本地事务,则XA语句不能被使用,直到该事务被提交或被 回滚为止。 ### 13.5. 数据库管理语句 [ 13.5.1. 账户管理语句](#)[ 13.5.2. 表维护语句](#)[13.5.3. SET语法](#)[13.5.4. SHOW语法](#)[ 13.5.5. 其它管理语句](#) ### 13.5.1. 账户管理语句 [13.5.1.1. CREATE USER语法](#)[13.5.1.2. DROP USER语法](#)[13.5.1.3. GRANT和REVOKE语法](#)[13.5.1.4. RENAME USER语法](#)[13.5.1.5. SET PASSWORD语法](#) #### 13.5.1.1. CREATE USER语法 ~~~ CREATE USER user [IDENTIFIED BY [PASSWORD] 'password'] ~~~ ~~~     [, user [IDENTIFIED BY [PASSWORD] 'password']] ... ~~~ CREATE USER用于创建新的MySQL账户。要使用CREATE USER,您必须拥有mysql数据库的全局CREATE USER权限,或拥有INSERT权限。对于每个账户,CREATE USER会在没有权限的mysql.user表中创建一个新记录。如果 账户已经存在,则出现错误。 使用自选的IDENTIFIED BY子句,可以为账户给定一个密码。user值和 密码的给定方法和GRANT语句一样。特别是,要在纯文本中指定密码,需忽略PASSWORD关键词。要把 密码指定为由PASSWORD()函数返回的混编值,需包含关键字PASSWORD。请参见[13.5.1.3节,“GRANT和REVOKE语法”](# "13.5.1.3. GRANT and REVOKE Syntax")。 #### 13.5.1.2. DROP USER语法 ~~~ DROP USER user [, user] ... ~~~ DROP USER语句用于删除一个或多个MySQL账户。要使用DROP USER,您必须拥有mysql数据库的全局CREATE USER权限或DELETE权限。使用与GRANT或REVOKE相同的格式为每个 账户命名;例如,'jeffrey'@'localhost'。 账户名称的用户和主机部分与用户表记录的User和Host列值相对应。 使用DROP USER,您可以取消一个账户和其权限,操作如下: ~~~ DROP USER user; ~~~ 该语句可以删除来自所有授权表的帐户权限记录。 要点:DROP USER不能自动关闭任何打开的用户对话。而且,如果用户有打开的对话,此时取消用户,则命令不会生效,直到用户对话被关闭后才生效。一旦对话被关闭,用户也被取消,此用户再次试图登录时将会失败。这是有意设计的。 #### 13.5.1.3. GRANT和REVOKE语法 ~~~ GRANT priv_type [(column_list)] [, priv_type [(column_list)]] ... ON [object_type] {tbl_name | * | *.* | db_name.*} TO user [IDENTIFIED BY [PASSWORD] 'password'] [, user [IDENTIFIED BY [PASSWORD] 'password']] ... [REQUIRE NONE | [{SSL| X509}] [CIPHER 'cipher' [AND]] [ISSUER 'issuer' [AND]] [SUBJECT 'subject']] [WITH with_option [with_option] ...] object_type = TABLE | FUNCTION | PROCEDURE with_option = GRANT OPTION | MAX_QUERIES_PER_HOUR count | MAX_UPDATES_PER_HOUR count | MAX_CONNECTIONS_PER_HOUR count | MAX_USER_CONNECTIONS count ~~~ ~~~ REVOKE priv_type [(column_list)] [, priv_type [(column_list)]] ... ON [object_type] {tbl_name | * | *.* | db_name.*} FROM user [, user] ... REVOKE ALL PRIVILEGES, GRANT OPTION FROM user [, user] ... ~~~ GRANT和REVOKE语句允许系统管理员创建MySQL用户 账户,授予权限和撤销权限。 MySQL账户信息存储在mysql数据库的表中。在[第5章:](#)[*数据库管理*](# "Chapter 5. Database Administration")中对本数据库和访问控制系统进行了详尽的讨论。要了解更多详细信息,您应该查询此章。 如果授权表拥有含有mixed-case数据库或表名称的权限记录,并且lower_case_table_names系统变量已设置,则不能使用REVOKE撤销权限,必须直接操纵授权表。(当lower_case_table_names已设置时,GRANT将不会创建此类记录,但是此类记录可能已经在设置变量之前被创建了。) 授予的权限可以分为多个层级: ·         **全局层级** 全局权限适用于一个给定服务器中的所有数据库。这些权限存储在mysql.user表中。GRANT ALL ON *.*和REVOKE ALL ON *.*只授予和撤销全局权限。 ·         **数据库层级** 数据库权限适用于一个给定数据库中的所有目标。这些权限存储在mysql.db和mysql.host表中。GRANT ALL ON *db_name*.*和REVOKE ALL ON *db_name*.*只授予和撤销数据库权限。 ·         **表层级** 表权限适用于一个给定表中的所有列。这些权限存储在mysql.talbes_priv表中。GRANT ALL ON *db_name.tbl_name*和REVOKE ALL ON *db_name.tbl_name*只授予和撤销表权限。 ·         **列层级** 列权限适用于一个给定表中的单一列。这些权限存储在mysql.columns_priv表中。当使用REVOKE时,您必须指定与被授权列相同的列。 ·         **子程序层级** CREATE ROUTINE, ALTER ROUTINE, EXECUTE和GRANT权限适用于已存储的子程序。这些权限可以被授予为全局层级和数据库层级。而且,除了CREATE ROUTINE外,这些权限可以被授予为子程序层级,并存储在mysql.procs_priv表中。 当后续目标是一个表、一个已存储的函数或一个已存储的过程时,*object_type*子句应被指定为TABLE、FUNCTION或PROCEDURE。当从旧版本的MySQL升级时,要使用本子句,您必须升级您的授权表。请参见[2.10.2节,“升级授权表”](# "2.10.2. Upgrading the Grant Tables")。 要使用GRANT或REVOKE,您必须拥有GRANT OPTION权限,并且您必须用于您正在授予或撤销的权限。 要撤销所有权限,需使用以下语法。此语法用于取消对于已命名的用户的所有全局层级、数据库层级、表层级和列层级的权限。 ~~~ REVOKE ALL PRIVILEGES, GRANT OPTION FROM user [, user] ... ~~~ 要使用本REVOKE语法,您必须拥有mysql数据库的全局CREATE USER权限或UPDATE权限。 对于GRANT和REVOKE语句,priv_type可以被指定为以下任何一种: <table border="1" cellpadding="0" id="table7"><tr><td> <p><strong> <span>权限</span></strong></p></td> <td> <p><strong> <span>意义</span></strong></p></td> </tr><tr><td> <p> <span>ALL [PRIVILEGES]</span></p></td> <td> <p><span>设置除<span>GRANT OPTION</span>之外的所有简单权限</span></p></td> </tr><tr><td> <p> <span>ALTER</span></p></td> <td> <p><span>允许使用<span>ALTER TABLE</span></span></p></td> </tr><tr><td> <p> <span>ALTER ROUTINE</span></p></td> <td> <p>更改或取消已存储的子程序</p></td> </tr><tr><td> <p> <span>CREATE</span></p></td> <td> <p><span>允许使用<span>CREATE TABLE</span></span></p></td> </tr><tr><td> <p> <span>CREATE ROUTINE</span></p></td> <td> <p>创建已存储的子程序</p></td> </tr><tr><td> <p> <span>CREATE TEMPORARY TABLES</span></p></td> <td> <p><span>允许使用<span>CREATE TEMPORARY TABLE</span></span></p></td> </tr><tr><td> <p> <span>CREATE USER</span></p></td> <td> <p>允许使用<span>CREATE USER, DROP USER, RENAME USER</span>和<span>REVOKE ALL PRIVILEGES</span>。</p></td> </tr><tr><td> <p> <span>CREATE VIEW</span></p></td> <td> <p><span>允许使用<span>CREATE VIEW</span></span></p></td> </tr><tr><td> <p> <span>DELETE</span></p></td> <td> <p><span>允许使用<span>DELETE</span></span></p></td> </tr><tr><td> <p> <span>DROP</span></p></td> <td> <p><span>允许使用<span>DROP TABLE</span></span></p></td> </tr><tr><td> <p> <span>EXECUTE</span></p></td> <td> <p>允许用户运行已存储的子程序</p></td> </tr><tr><td> <p> <span>FILE</span></p></td> <td> <p><span>允许使用<span>SELECT...INTO OUTFILE</span>和<span>LOAD DATA INFILE</span></span></p></td> </tr><tr><td> <p> <span>INDEX</span></p></td> <td> <p><span>允许使用<span>CREATE INDEX</span>和<span>DROP INDEX</span></span></p></td> </tr><tr><td> <p> <span>INSERT</span></p></td> <td> <p><span>允许使用<span>INSERT</span></span></p></td> </tr><tr><td> <p> <span>LOCK TABLES</span></p></td> <td> <p>允许对您拥有<span>SELECT</span>权限的表使用<span>LOCK TABLES</span></p></td> </tr><tr><td> <p> <span>PROCESS</span></p></td> <td> <p><span>允许使用<span>SHOW FULL PROCESSLIST</span></span></p></td> </tr><tr><td> <p> <span>REFERENCES</span></p></td> <td> <p>未被实施</p></td> </tr><tr><td> <p> <span>RELOAD</span></p></td> <td> <p><span>允许使用<span>FLUSH</span></span></p></td> </tr><tr><td> <p> <span>REPLICATION CLIENT</span></p></td> <td> <p>允许用户询问从属服务器或主服务器的地址</p></td> </tr><tr><td> <p> <span>REPLICATION SLAVE</span></p></td> <td> <p>用于复制型从属服务器(从主服务器中读取二进制日志事件)</p></td> </tr><tr><td> <p> <span>SELECT</span></p></td> <td> <p><span>允许使用<span>SELECT</span></span></p></td> </tr><tr><td> <p> <span>SHOW DATABASES</span></p></td> <td> <p><span>SHOW DATABASES</span>显示所有数据库</p></td> </tr><tr><td> <p> <span>SHOW VIEW</span></p></td> <td> <p><span>允许使用<span>SHOW CREATE VIEW</span></span></p></td> </tr><tr><td> <p> <span>SHUTDOWN</span></p></td> <td> <p>允许使用<strong><span>mysqladmin shutdown</span></strong></p></td> </tr><tr><td> <p> <span>SUPER</span></p></td> <td> <p>允许使用<span>CHANGE MASTER, KILL, PURGE MASTER LOGS</span>和<span>SET GLOBAL</span>语句,<strong><span>mysqladmin debug</span></strong>命令;允许您连接(一次),即使已达到<span>max_connections</span>。</p></td> </tr><tr><td> <p> <span>UPDATE</span></p></td> <td> <p><span>允许使用<span>UPDATE</span></span></p></td> </tr><tr><td> <p> <span>USAGE</span></p></td> <td> <p>“无权限”的同义词</p></td> </tr><tr><td> <p> <span>GRANT OPTION</span></p></td> <td> <p>允许授予权限</p></td> </tr></table> 当从旧版本的MySQL升级时,要使用EXECUTE, CREATE VIEW, SHOW VIEW, CREATE USER, CREATE ROUTINE和ALTER ROUTINE权限,您必须首先升级您的授权表。请参见[2.10.2节,“升级授权表”](# "2.10.2. Upgrading the Grant Tables")。 REFERENCES权限目前未被使用。 当您想要创建一个没有权限的用户时,可以指定USAGE。 使用SHOW GRANTS来确定帐户拥有什么权限。请参见[13.5.4.10节,“SHOW GRANTS语法”](# "13.5.4.10. SHOW GRANTS Syntax")。 您可以通过使用ON *.*语法赋予全局权限,或通过使用ON *db_name*.*语法赋予数据库层级权限。如果您指定了ON *并且您已经选择了一个默认数据库,则权限被赋予到这个数据库中。(警告:如果您指定了ON *同时您没有选择一个默认数据库,则权限是全局的。) FILE, PROCESS, RELOAD, REPLICATION CLIENT, REPLICATION SLAVE, SHOW DATABASES, SHUTDOWN和SUPER权限是管理性权限,只能进行全局授权(使用ON *.*语法)。 其它权限可以被全局授权,或被赋予为其它层级。 对于一个表,您可以指定的priv_type值只能是SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, GRANT OPTION, INDEX和ALTER。 对于一个列(也就是,当您使用一个column_list子句时),您可以指定的priv_type值只能是SELECT, INSERT和UPDATE。 在子程序层级,您可以指定的priv_type值只能是ALTER ROUTINE, EXECUTE和GRANT OPTION。CREATE ROUTINE不是一个子程序层级的权限,因为您必须拥有此权限,才能创建一个子程序。 对于全局、数据库、表和子程序层级,GRANT ALL只能赋予在您正在授权的层级中存在的权限。例如,如果您使用GRANT ALL ON *db_name*.*,这是一个数据库层级语句,因此不会授予全局权限,如FILE等。 MySQL允许您对不存在的数据库目标授予权限。在此情况下,将被授予的权限必须包括CREATE权限。这个性质是有意设计的,目的是允许数据库管理员为将在此后被创建的数据库目标预备用户 账户和权限。 要点:当您取消一个表或数据库时,MySQL不会自动撤销任何权限。但是,如果您取消一个子程序,则被赋予该子程序的所有子程序层级的权限都被撤销。 注意:GRANT语句用于在全局层级或数据库层级赋予权限。当在GRANT语句中指定数据库名称时,允许使用‘_’和‘%’通配符。这意味着,如果您想要使用‘_’字符作为一个数据库名称的一部分,您应该在GRANT语句中指定它为‘\_’,以防止用户可以访问其它符合此通配符格式的数据库;例如,GRANT ... ON `foo\_bar`.* TO ...。 为了接纳对来自任意主机的用户授权的权利,MySQL支持以*user_name*@*host_name*的形式指定user值。如果一个user_name或host_name与一个不加引号的标识符一样是合法的,那么您不需要对它加引号。不过,要指定一个包含特殊字符(如‘-’)的user_name字符串,或一个包含特殊字符或通配字符(如‘%’),则引号是必要的;例如,'test-user'@'test-hostname'。分别对username和hostname加引号。 您可以在hostname中指定通配符。例如*user_name*@'%.loc.gov'适用于在loc.gov域中的任何主机的user_name。同时*user_name*@'144.155.166.%'适用于144.155.166 C级子网中的任何主机的user_name。 简单形式user_name是*user_name*@'%'的同义词。 MySQL不支持usernames中的通配符。通过把带有User=''的登录项插入到mysql.user表中,或通过使用GRANT语句创建一个带有空名称的用户,可以定义匿名用户: ~~~ mysql> GRANT ALL ON test.* TO ''@'localhost' ... ~~~ 当把带引号的值是,需使用反勾号(‘`’)为数据库、表、列和子程序名称加引号。使用单引号(‘'’)为hostnames、usernames和 密码加引号。 警告:如果您允许匿名用户连接到MySQL服务器,则您应该同时向所有本地用户授予*user_name*@localhost权限。否则,当有名称的用户试图从本地机器登录MySQL服务器时,mysql.user表中的用于localhost的匿名用户帐户会被使用。 您可以通过执行以下查询来确定是否这适合于您。以下查询列举了所有匿名用户: ~~~ mysql> SELECT Host, User FROM mysql.user WHERE User=''; ~~~ 如果您想要删除本地匿名用户账户,以避免出现刚才谈到的问题,则需使用以下语句: ~~~ mysql> DELETE FROM mysql.user WHERE Host='localhost' AND User=''; ~~~ ~~~ mysql> FLUSH PRIVILEGES; ~~~ GRANT支持最长为60个字符的hostnames。数据库、表、列和子程序名称最长可为64个字符。Usernames最长可为16个字符。 注释:不能通过更改mysql.user表来改变usernames的允许长度。如果试图这么做,会导致出现不可预见的问题,可能会造成用户无法登录MySQL服务器。除了采用由MySQL公司提供的用于升级MySQL服务器的mysql_fix_privilege_tables原稿之外,请您不要以任何方式变更授权表。 对于表或列的权限是作为各个权限层级的逻辑OR权限被附加形成的。例如,如果mysql.user表指定一个用户拥有全局SELECT权限,则该权限不能被数据库、表或列层级的登录项定义。 可以按下列方法计算列权限: ~~~ global privileges ~~~ ~~~ OR (database privileges AND host privileges) ~~~ ~~~ OR table privileges ~~~ ~~~ OR column privileges ~~~ 在多数情况下,您只在一个权限层级下向用户授予权利,所以寿命通常不是那么复杂。有关权限检查规程的细节,请参见[5.7节,“MySQL访问权限系统”](# "5.7. The MySQL Access Privilege System")。 如果您对一个在mysql.user表中不存在的username/hostname组合授予权限,则增加一个登录项并保持在此处,直到使用DELETE语句删除为止。换句话说,GRANT可以创建用户表登录项,但是REVOKE不会取消它们;您必须使用DROP USER或DELETE明确地操作。 如果创建了一个新的用户,或者如果您拥有全局授权权限,则用户密码被设置为由IDENTIFIED BY子句指定的密码(如果给定了一个)。如果用户已拥有了一个密码,则此密码被新密码替代。 警告:如果您创建了一个新用户,但是不指定IDENTIFIED BY子句,则用户没有 密码。这是很不安全的。不过,您可以启用NO_AUTO_CREATE_USER SQL模式,来防止GRANT创建一个新用户(否则GRANT会这么做),除非给定了IDENTIFIED BY来为新用户提供一个密码。 使用SET PASSWORD语句也可以设置密码。请参见[13.5.1.5节,“SET PASSWORD语法”](# "13.5.1.5. SET PASSWORD Syntax")。 在IDENTIFIED BY子句中,密码应被作为文字密码只被给定。没有必要使用PASSWORD()函数,因为该函数用于SET PASSWORD语句。例如: ~~~ GRANT ... IDENTIFIED BY 'mypass'; ~~~ 如果您不想以明白的文字发送密码,并且您知道PASSWORD()返回给密码的混编值,则您可以指定混编值,前面加入关键词PASSWORD: ~~~ GRANT ... ~~~ ~~~ IDENTIFIED BY PASSWORD '*6C8989366EAF75BB670AD8EA7A7FC1176A95CEF4'; ~~~ 在一个C程序中,您可以通过使用make_scrambled_password() C API函数得到混编值。 如果您为一个数据库授予权限,则在mysql.db表中,会根据需要创建登录项。如果使用REVOKE删除了所有的数据库权限,则本登录项被删除。 如果一个用户不拥有表权限,则当用户申请表清单时(例如,使用SHOW TABLES语句),表名称不显示。 SHOW DATABASES权限允许账户通过发布SHOW DATABASE语句来观看数据名称。不拥有此权限的账户只能看到他们拥有部分权限的数据库,并且如果使用--skip-show-database选项启动服务器,则根本不能使用本语句。 WITH GRANT OPTION子句给予用户能力,可以在指定的权限层级,向其它用户给定其拥有的任何权限。您应该留心您给予了谁GRANT OPTION权限,因为拥有不同权限的两个用户可以联合使用权限! 您不能向其它用户授予您自己没有的权限;GRANT OPTION权限只允许您赋予您自己拥有的权限。 要注意,当您在某个特定权限层级向一个用户授予GRANT OPTION权限时,用户拥有的该层级的任何权限(或未来将被给定的权限)也可以由该用户授予。假设您向一个用户赋予了数据库INSERT权限。如果您然后赋予数据库SELECT权限,并指定了WITH GRANT OPTION,则该用户不仅可以向其它用户给予SELECT权限,还可以给予INSERT。如果您然后向用户授予数据库UPDATE权限,则用户可以授予INSERT, SELECT和UPDATE。 您不应该向一个常规用户授予ALTER权限。如果您这么做,则该用户可以尝试通过对表重新命名来破坏授权系统! The MAX_QUERIES_PER_HOUR *count*, MAX_UPDATES_PER_HOUR *count*, and MAX_CONNECTIONS_PER_HOUR * count* options limit the number of queries, updates, and logins a user can perform during any given one-hour period. If *count* is 0 (the default), this means that there is no limitation for that user.      MAX_QUERIES_PER_HOUR *count*, MAX_UPDATES_PER_HOUR *count*和MAX_CONNECTIONS_PER_HOUR *count*选项限制了在任何给定的一小时期间,用户可以执行的查询、更新和登录的数目。如果count是0(默认值),这意味着,对该用户没有限制。 MAX_USER_CONNECTIONS *count*选项限制了账户可以同时进行的连接的最大数目。如果count是0(默认值),则max_user_connections系统可以决定该 账户同时连接的数目。 注释:要对一个原有的用户指定任何这类资源限制型选项,同时又不影响原有的权限,需使用GRANT USAGE ON *.* ... WITH MAX_...。 见[5.8.4节,“限制账户资源”](# "5.8.4. Limiting Account Resources")。 除了根据username和密码进行常规鉴定外,MySQL还可以检查X509证明属性。要为MySQL账户指定与SSL有关的选项,需使用GRANT语句的REQUIRE子句。(要了解有关在MySQL中使用SSL的背景信息,请参见[5.8.7节,“使用安全连接”](# "5.8.7. Using Secure Connections")。) 对于一个给定的账户,有多种可能性可以限制连接类型: ·         如果账户没有SSL或X509要求,并且如果username和 密码是有效的,则允许不加密连接。但是,如果客户端有正确的证明和关键文件,则根据客户端的选择,也可以使用加密连接。 ·         REQUIRE SSL选项用于告知服务器,对于该账户只允许SSL加密连接。注意,如果有允许任何非SSL连接的访问控制记录,则本选项可以被忽略。 ~~~ ·                mysql> GRANT ALL PRIVILEGES ON test.* TO 'root'@'localhost' ~~~ ~~~ ·                    -> IDENTIFIED BY 'goodsecret' REQUIRE SSL; ~~~ ·         REQUIRE X509意味着客户端必须拥有一个有效证明,除非不需要确切的证明、发布者和主题。唯一的要求是,应可以使用CA证明其中之一来验证签名。 ~~~ ·                mysql> GRANT ALL PRIVILEGES ON test.* TO 'root'@'localhost' ~~~ ~~~ ·                    -> IDENTIFIED BY 'goodsecret' REQUIRE X509; ~~~ ·         REQUIRE ISSUER '*issuer*'用于对连接尝试进行限定,客户端必须出示一个由CA’issuer’发布的有效的X509证明。如果客户端出示的证明是有效的,但是有一个不同的发布者,则服务器会拒绝连接。使用X509证明就意味着要加密,所以在这种情况下,SSL选项是不必要的。 ~~~ ·                mysql> GRANT ALL PRIVILEGES ON test.* TO 'root'@'localhost' ~~~ ~~~ ·                    -> IDENTIFIED BY 'goodsecret' ~~~ ~~~ ·                    -> REQUIRE ISSUER '/C=FI/ST=Some-State/L=Helsinki/ ~~~ ~~~ ·                       O=MySQL Finland AB/CN=Tonu Samuel/Email=tonu@example.com'; ~~~ 注意,ISSUER值应被作为一个单一字符串输入。 ·         REQUIRE SUBJECT '*subject*'用于对连接尝试进行限定,客户端必须出示一个包含主题subject的有效的X509证明。如果客户端出示的证明是有效的,但是有一个不同的主题,则服务器会拒绝连接。 ~~~ ·                mysql> GRANT ALL PRIVILEGES ON test.* TO 'root'@'localhost' ~~~ ~~~ ·                    -> IDENTIFIED BY 'goodsecret' ~~~ ~~~ ·                    -> REQUIRE SUBJECT '/C=EE/ST=Some-State/L=Tallinn/ ~~~ ~~~ ·                       O=MySQL demo client certificate/ ~~~ ~~~ ·                       CN=Tonu Samuel/Email=tonu@example.com'; ~~~ 注意,SUBJECT值应被作为一个单一字符串输入。 ·         需要REQUIRE CIPHER '*cipher*'来确认使用了密码和足够长度的关键字。如果使用了采用短型加密关键字的旧算法,SSL本身会比较脆弱。使用本选项,您可以要求使用特定的密码方法来许可一个连接。 ~~~ ·                mysql> GRANT ALL PRIVILEGES ON test.* TO 'root'@'localhost' ~~~ ~~~ ·                    -> IDENTIFIED BY 'goodsecret' ~~~ ~~~ ·                    -> REQUIRE CIPHER 'EDH-RSA-DES-CBC3-SHA'; ~~~ SUBJECT, ISSUER和CIPHER选项可以在REQUIRE子句中结合,如下: ~~~ mysql> GRANT ALL PRIVILEGES ON test.* TO 'root'@'localhost' ~~~ ~~~     -> IDENTIFIED BY 'goodsecret' ~~~ ~~~     -> REQUIRE SUBJECT '/C=EE/ST=Some-State/L=Tallinn/ ~~~ ~~~        O=MySQL demo client certificate/ ~~~ ~~~        CN=Tonu Samuel/Email=tonu@example.com' ~~~ ~~~     -> AND ISSUER '/C=FI/ST=Some-State/L=Helsinki/ ~~~ ~~~        O=MySQL Finland AB/CN=Tonu Samuel/Email=tonu@example.com' ~~~ ~~~     -> AND CIPHER 'EDH-RSA-DES-CBC3-SHA'; ~~~ 注意,SUBJECT和ISSUER值各自应被作为一个单一字符串输入。 在REQUIRE各选项之间,AND关键词是自选的。 选项的顺序无所谓,但是选项不能被指定两次。 当mysqld启动后,所有的权限被读入存储器中。要了解详细说明,请参见[5.7.7节,“权限更改何时生效”](# "5.7.7. When Privilege Changes Take Effect")。 注意,如果您正在使用表权限或列权限,即使只对一个用户使用,服务器也会对所有用户检查表权限和列权限,这会略微降低MySQL的速度。与此类似,如果您对某些用户限制查询、更新或连接的数目,则服务器必须监测这些值。 标准SQL版本和MySQL版本的GRANT之间的最大区别是: ·         在MySQL中,权限与hostname和username的组合有关,与 单一的username无关。 ·         标准SQL不拥有全局层级或数据库层级权限,也不支持MySQL支持的所有权限类型。 ·         MySQL不支持标准SQL TRIGGER或UNDER权限。 ·         标准SQL权限以一种分等级的方式进行组织。如果您取消一个用户,则用户被授予的所有权限都被撤销。在MySQL中,如果您使用DROP USER,也会如此。请参见[13.5.1.2节,“DROP USER语法”](# "13.5.1.2. DROP USER Syntax")。 ·         在标准SQL中,当您取消一个表时,对一个表的所有权限会被撤销。在标准SQL中,当您撤销一个权限时,根据该权限被授予的所有权限也会被撤销。在MySQL中,只有使用明确的REVOKE语句,或通过操作存储在MySQL授权表中的值,才能取消权限。 ·         在MySQL中,可以只对一个表中的部分列拥有INSERT权限。在此情况下,如果您忽略您不拥有INSERT权限的那些列,,您仍然可以对表执行INSERT语句。如果没有启用严格的SQL模式,则被忽略的列被设置为各自隐含的默认值。在严格模式下,如果某个被忽略的列没有默认值,则该语句被拒绝。[5.3.2节,“SQL服务器模式”](# "5.3.2. The Server SQL Mode")对严格模式进行了讨论。[13.1.5节,“CREATE TABLE语法”](# "13.1.5. CREATE TABLE Syntax")对隐含默认值进行了讨论。 您不拥有INSERT权限的列被设置为各自的默认值。标准SQL要求您拥有所有列的INSERT权限。 在MySQL中,如果您只拥有一个表中的部分列的INSERT权限,同时,如果您从INSERT语句中忽略您不拥有权限的列,则您仍然可以对表执行INSERT语句;那些列将被设置为各自的默认值。在严格模式下(即当sql_mode='traditional'时,如果某些被忽略的列没有默认值,则INSERT语句将被拒绝。 #### 13.5.1.4. RENAME USER语法 ~~~ RENAME USER old_user TO new_user ~~~ ~~~     [, old_user TO new_user] ... ~~~ RENAME USER语句用于对原有MySQL账户进行重命名。要使用RENAME USER,您必须拥有全局CREATE USER权限或mysql数据库UPDATE权限。如果旧 账户不存在或者新账户已存在,则会出现错误。*old_user*和*new_user*值的给定方法与GRANT语句一样。 #### 13.5.1.5. SET PASSWORD语法 ~~~ SET PASSWORD = PASSWORD('some password') ~~~ ~~~ SET PASSWORD FOR user = PASSWORD('some password') ~~~ SET PASSWORD语句用于向一个原有MySQL用户 账户赋予一个密码。 第一个语法为当前用户设置密码。已使用一个非匿名账户连接到服务器上的任何客户即都可以更改该账户的密码。 第二个语法为当前服务器主机上的一个特定账户设置密码。只有拥有mysql数据库UPDATE权限的客户端可以这么做。user值应以*user_name*@*host_name*的格式被给定,此处*user_name*和*host_name*与mysql.user表登录项的User和Host列中列出的完全一样。举例说明,如果您有一个登录项,User和Host列值为'bob'和'%.loc.gov',您应该按如下方法写语句: ~~~ mysql> SET PASSWORD FOR 'bob'@'%.loc.gov' = PASSWORD('newpass'); ~~~ 这相当于以下语句: ~~~ mysql> UPDATE mysql.user SET Password=PASSWORD('newpass') ~~~ ~~~     -> WHERE User='bob' AND Host='%.loc.gov'; ~~~ ~~~ mysql> FLUSH PRIVILEGES; ~~~ 注释:如果您正在使用一个4.1以前的客户端连接到一个MySQL 4.1或MySQL 4.1以后的服务器,则在阅读[5.7.9节,“MySQL 4.1中的密码哈希处理”](# "5.7.9. Password Hashing in MySQL 4.1")之前,不能使用前面的SET PASSWORD或UPDATE语句。 密码格式在MySQL 4.1中变更了,并且在特定情况下,如果您更改密码,您可能无法在连接到服务器上。 您可以通过执行SELECT CURRENT_USER()观看您当前的鉴定user@host登录项。 ### 13.5.2. 表维护语句 [13.5.2.1. ANALYZE TABLE语法](#)[13.5.2.2. BACKUP TABLE语法](#)[13.5.2.3. CHECK TABLE语法](#)[13.5.2.4. CHECKSUM TABLE语法](#)[13.5.2.5. OPTIMIZE TABLE语法](#)[13.5.2.6. REPAIR TABLE语法](#)[13.5.2.7. RESTORE TABLE语法](#) #### 13.5.2.1. ANALYZE TABLE语法 ~~~ ANALYZE [LOCAL | NO_WRITE_TO_BINLOG] TABLE tbl_name [, tbl_name] ... ~~~ 本语句用于分析和存储表的关键字分布。在分析期间,使用一个读取锁定对表进行锁定。这对于MyISAM, BDB和InnoDB表有作用。对于MyISAM表,本语句与使用**myisamchk -a**相当。 MySQL使用已存储的关键字分布来决定,当您对除常数以外的对象执行联合时,表按什么顺序进行联合。 本语句会返回一个含有以下列的表: <table border="1" cellpadding="0" id="table8"><tr><td> <p><strong> <span>列</span></strong></p></td> <td> <p><strong> <span>值</span></strong></p></td> </tr><tr><td> <p> <span>Table</span></p></td> <td> <p>表名称</p></td> </tr><tr><td> <p> <span>Op</span></p></td> <td> <p><span>进行分析</span></p></td> </tr><tr><td> <p> <span>Msg_type</span></p></td> <td> <p><span> 状态、错误、信息或警告之一</span></p></td> </tr><tr><td> <p> <span>Msg_text</span></p></td> <td> <p>消息</p></td> </tr></table> 您可以使用SHOW INDEX语句检查已存储的关键字分布。请参见[13.5.4.11节,“SHOW INDEX语法”](# "13.5.4.11. SHOW INDEX Syntax")。 如果从上一个ANALYZE TABLE语句开始,表没有变化,则不再分析该表。 ANALYZE TABLE语句被写入二进制日志中,除非使用了自选的NO_WRITE_TO_BINLOG关键词(或其别名LOCAL)。 #### 13.5.2.2. BACKUP TABLE语法 ~~~ BACKUP TABLE tbl_name [, tbl_name] ... TO '/path/to/backup/directory' ~~~ 注释:本语句不理想。我们正在努力寻找一种更好的替代方式,该方式将提供在线备份能力。同时,也可以使用**mysqlhotcopy**原本替代。 BACKUP TABLE用于在刷新了所有对磁盘的缓冲变更后,把恢复表所需的最少数目的表文件拷贝到备份目录中。本语句只对MyISAM表起作用。它可以拷贝.frm定义文件和.MYD数据文件。.MYI索引文件可以从这两个文件中重建。本目录应被指定为一个完整的路径名。 在使用本语句前,请参见[5.9.1节,“数据库备份”](# "5.9.1. Database Backups")。 在备份期间,为每个表保持一个读取锁定,每次一个,在正在备份时锁定。如果您想要把多个表作为一个快照来备份(防止它们在备份操作过程中被更改),您必须实现发布一个LOCK TABLES语句,以获得对一个组群中的每个表的读取锁定。 该语句会返回一个含有以下列的表: <table border="1" cellpadding="0" id="table9"><tr><td> <p><strong> <span>列</span></strong></p></td> <td> <p><strong> <span>值</span></strong></p></td> </tr><tr><td> <p> <span>Table</span></p></td> <td> <p>表名称</p></td> </tr><tr><td> <p> <span>Op</span></p></td> <td> <p><span>进行备份</span></p></td> </tr><tr><td> <p> <span>Msg_type</span></p></td> <td> <p><span> 状态、错误、信息或警告之一</span></p></td> </tr><tr><td> <p> <span>Msg_text</span></p></td> <td> <p>消息</p></td> </tr></table> #### 13.5.2.3. CHECK TABLE语法 ~~~ CHECK TABLE tbl_name [, tbl_name] ... [option] ... ~~~ ~~~   ~~~ ~~~ option = {QUICK | FAST | MEDIUM | EXTENDED | CHANGED} ~~~ 检查一个或多个表是否有错误。CHECK TABLE对MyISAM和InnoDB表有作用。对于MyISAM表,关键字统计数据被更新。 CHECK TABLE也可以检查视图是否有错误,比如在视图定义中被引用的表已不存在。 CHECK TABLE语句会返回一个含有以下列的表: <table border="1" cellpadding="0" id="table10"><tr><td> <p><strong> <span>列</span></strong></p></td> <td> <p><strong> <span>值</span></strong></p></td> </tr><tr><td> <p> <span>Table</span></p></td> <td> <p>表名称</p></td> </tr><tr><td> <p> <span>Op</span></p></td> <td> <p><span>进行检查</span></p></td> </tr><tr><td> <p> <span>Msg_type</span></p></td> <td> <p><span> 状态、错误、信息或错误之一</span></p></td> </tr><tr><td> <p> <span>Msg_text</span></p></td> <td> <p>消息</p></td> </tr></table> 注意,该语句可能会为每个被检查的表产生多行信息。最后一行有一个Msg_type状态值。Msg_text通常应为OK。如果您没有得到OK,或表已经更新了,则您通常应该运行修复后的表。请参见[5.9.4节,“表维护和崩溃恢复”](# "5.9.4. Table Maintenance and Crash Recovery")。表已经更新了,这意味着表的存储引擎指示没有必要检查表。 可以给予的不同的检查选项列于下表中。这些选项只适用于检查MyISAM表。对于InnoDB表和视图,这些选项被忽略。 <table border="1" cellpadding="0" id="table11"><tr><td> <p><strong> <span>类型</span></strong></p></td> <td> <p><strong> <span>意义</span></strong></p></td> </tr><tr><td> <p> <span>QUICK</span></p></td> <td> <p>不扫描行,不检查错误的链接。</p></td> </tr><tr><td> <p> <span>FAST</span></p></td> <td> <p>只检查没有被正确关闭的表。</p></td> </tr><tr><td> <p> <span>CHANGED</span></p></td> <td> <p>只检查上次检查后被更改的表,和没有被正确关闭的表。</p></td> </tr><tr><td> <p> <span>MEDIUM</span></p></td> <td> <p> 扫描行,以验证被删除的链接是有效的。也可以计算各行的关键字校验和,并使用计算出的校验和验证这一点。</p></td> </tr><tr><td> <p> <span>EXTENDED</span></p></td> <td> <p>对每行的所有关键字进行一个全面的关键字查找。这可以确保表是<span>100</span>%一致的,但是花的时间较长。</p></td> </tr></table> 如果没有指定QUICK, MEDIUM或EXTENDED选项,则对于动态格式MyISAM表,默认检查类型是MEDIUM。这与对表运行**myisamchk --medium-check *****tbl_name***的结果相同。对于静态格式MyISAM表,默认检查类型也是MEDIUM,除非CHANGED或FAST已被指定。在此情况下,默认值为QUICK。对于CHANGED和FAST,行扫描被跳过,因为行极少被破坏。 您可以组合检查选项,如下面的例子所示。该例子对表进行了一个快速检查,来查看该表是否被正确关闭: ~~~ CHECK TABLE test_table FAST QUICK; ~~~ 注释:在有些情况下,CHECK TABLE会更改表。如果表被标记为“corrupted”或“not closed properly”,则出现这种情况。但是CHECK TABLE不会找出表中的问题。在这种情况下,CHECK TABLE会把表标记为良好。 如果一个表被破坏,很有可能问题在索引中,而不在数据部分中。所有前述的检查类型都可以彻底地检查索引,因此,可以找出多数的错误。 如果您只想要检查您假定的表是良好的,您应该不使用检查选项或QUICK选项。当您时间匆忙时,应使用QUICK。QUICK无法找出数据文件中的错误的风险非常小。(在多数情况下,在正常使用中,MySQL应能在数据文件中找出错误。如果找出了错误,表被标记为“corrupted”,并不能被使用,直到修复为止。) 如果您想要时常检查表,FAST和CHANGED多数情况下从原本中被使用(例如,从cron中被执行)。在多数情况下,FAST优先于CHANGED。(只有一种情况FAST不优先于CHANGED,那就是当您怀疑您在MyISAM代码中发现了错误。) 当MySQL试图通过关键字更新一行或查找一行时,如果您已经运行了一个常规检查后但仍得到来自表的奇怪的错误,此时使用EXTENDED。(如果常规的检查运行成功,则基本用不着EXTENDED。) 被CHECK TABLE报告的部分问题不会被自动修正: ·         发现行。此行中,auto_increment列有0值。 这意味着,您在表中有一行,该行的AUTO_INCREMENT索引列包含0值。(可以通过使用UPDATE语句,明确地把列设置为0,以创建一个AUTO_INCREMENT列为0的行。) 这本身不是一个错误,但是如果您决定转储表并恢复表,或对表进行ALTER TABLE,那么会导致出现麻烦。在此情况下,AUTO_INCREMENT列会根据AUTO_INCREMENT列的结果更改值,这会导致出现问题,如重复关键字错误等。 要消除警告,只需执行一个UPDATE语句,把列设置为除0以外的值。 #### 13.5.2.4. CHECKSUM TABLE语法 ~~~ CHECKSUM TABLE tbl_name [, tbl_name] ... [ QUICK | EXTENDED ] ~~~ 报告一个表校验和。 如果指定了QUICK,则报告活性表校验和,否则报告NULL。这是非常快的。活性表通过指定CHECKSUM=1表选项启用,目前只支持用于MyISAM表。请参见[13.1.5节,“CREATE TABLE语法”](# "13.1.5. CREATE TABLE Syntax")。 在EXTENDED模式下,整个表被一行一行地读取,并计算校验和。对于大型表,这是非常慢的。 默认情况下,如果既没有指定QUICK,也没有指定EXTENDED,并且如果表存储引擎支持,则MySQL返回一个活性校验和,否则会对表进行扫描。 CHECKSUM TABLE对于不存在的表会返回NULL。对于这种情况,会生成一个警告。 #### 13.5.2.5. OPTIMIZE TABLE语法 ~~~ OPTIMIZE [LOCAL | NO_WRITE_TO_BINLOG] TABLE tbl_name [, tbl_name] ... ~~~ 如果您已经删除了表的一大部分,或者如果您已经对含有可变长度行的表(含有VARCHAR, BLOB或TEXT列的表)进行了很多更改,则应使用OPTIMIZE TABLE。被删除的记录被保持在链接清单中,后续的INSERT操作会重新使用旧的记录位置。您可以使用OPTIMIZE TABLE来重新利用未使用的空间,并整理数据文件的碎片。 在多数的设置中,您根本不需要运行OPTIMIZE TABLE。即使您对可变长度的行进行了大量的更新,您也不需要经常运行,每周一次或每月一次即可,只对特定的表运行。 OPTIMIZE TABLE只对MyISAM, BDB和InnoDB表起作用。 对于MyISAM表,OPTIMIZE TABLE按如下方式操作: 1.    如果表已经删除或分解了行,则修复表。 2.    如果未对索引页进行分类,则进行分类。 3.       如果表的统计数据没有更新(并且通过对索引进行分类不能实现修复),则进行更新。 对于BDB表,OPTIMIZE TABLE目前被映射到ANALYZE TABLE上。对于InnoDB表,OPTIMIZE TABLE被映射到ALTER TABLE上,这会重建表。重建操作能更新索引统计数据并释放成簇索引中的未使用的空间。请参见[13.5.2.1节,“ANALYZE TABLE语法”](# "13.5.2.1. ANALYZE TABLE Syntax")。 使用—skip-new或—safe-mode选项可以启动mysqld。通过启动mysqld,您可以使OPTIMIZE TABLE对其它表类型起作用。 注意,在OPTIMIZE TABLE运行过程中,MySQL会锁定表。 OPTIMIZE TABLE语句被写入到二进制日志中,除非使用了自选的NO_WRITE_TO_BINLOG关键词(或其别名LOCAL)。已经这么做了,因此,用于MySQL服务器的OPTIMIZE TABLE命令的作用相当于一个复制主服务器,在默认情况下,这些命令将被复制到复制从属服务器中。 #### 13.5.2.6. REPAIR TABLE语法 ~~~ REPAIR [LOCAL | NO_WRITE_TO_BINLOG] TABLE ~~~ ~~~     tbl_name [, tbl_name] ... [QUICK] [EXTENDED] [USE_FRM] ~~~ REPAIR TABLE用于修复被破坏的表。默认情况下,REPAIR TABLE与**myisamchk --recover *****tbl_name***具有相同的效果。REPAIR TABLE对MyISAM和ARCHIVE表起作用。请参见[15.1节,“MyISAM存储引擎”](# "15.1. The MyISAM Storage Engine"), [ 15.8节,“ARCHIVE存储引擎”](# "15.8. The ARCHIVE Storage Engine")。 通常,您基本上不必运行此语句。但是,如果灾难发生,REPAIR TABLE很有可能从MyISAM表中找回所有数据。如果您的表经常被破坏,您应该尽力找到原因,以避免使用REPAIR TALBE。请参见[A.4.2节,“如果MySQL依然崩溃,应作些什么”](# "A.4.2. What to Do If MySQL Keeps Crashing")。同时也见[15.1.4节,“MyISAM表方面的问题”](# "15.1.4. MyISAM Table Problems")。 本语句会返回一个含有以下列的表: <table border="1" cellpadding="0" id="table12"><tr><td> <p><strong> <span>列</span></strong></p></td> <td> <p><strong> <span>值</span></strong></p></td> </tr><tr><td> <p> <span>Table</span></p></td> <td> <p>表名称</p></td> </tr><tr><td> <p> <span>Op</span></p></td> <td> <p><span>进行修复</span></p></td> </tr><tr><td> <p> <span>Msg_type</span></p></td> <td> <p><span> 状态、错误、信息或警告之一</span></p></td> </tr><tr><td> <p> <span>Msg_text</span></p></td> <td> <p>消息</p></td> </tr></table> 对于每个被修复的表,REPAIR TABLE语句会产生多行的信息。上一行含有一个Msg_type状态值。Msg_test通常应为OK。如果您没有得到OK,您应该尝试使用**myisamchk --safe-recover**修复表,因为REPAIR TABLE尚不会执行所有的**myisamchk**选项。我们计划在将来使它的灵活性更强。 如果给定了QUICK,则REPAIR TABLE会尝试只修复索引树。这种类型的修复与使用**myisamchk --recover --quick**相似。 如果您使用EXTENDED,则MySQL会一行一行地创建索引行,代替使用分类一次创建一个索引。这种类型的修复与使用**myisamchk --safe-recover**相似。 对于REPAIR TABLE,还有一种USE_FRM模式可以利用。如果.MYI索引文件缺失或标题被破坏,则使用此模式。在这种模式下,MySQL可以使用来自.frm文件重新创建.MYI文件。这种修复不能使用**myisamchk**来完成。 注释:只能在您不能使用常规REPAIR模式是,才能使用此模式。.MYI标题包含重要的表元数据(特别是,当前的AUTO_INCREMENT值和Delete链接)。这些元数据在REPAIR...USE_FRM中丢失。如果表被压缩,则不能使用USE_FRM。因为本信息也存储在.MYI文件中。 REPAIR TABLE语句被写入二进制日志中,除非使用了自选的NO_WRITE_TO_BINLOG关键词(或其别名LOCAL)。 警告:如果在REPAIR TABLE运行过程中,服务器停机,则在重新启动之后,在执行其它操作之前,您必须立刻对表再执行一个REPAIR TABLE语句。(通过制作一个备份来启动是一个好办法。)再最不利情况下,您可以有一个新的干净的索引文件,不含有关数据文件的信息。然后,您执行的下一个操作会覆盖数据文件。这很少发生,但是是有可能的。 #### 13.5.2.7. RESTORE TABLE语法 ~~~ RESTORE TABLE tbl_name [, tbl_name] ... FROM '/path/to/backup/directory' ~~~ 用于恢复来自用BACKUP TABLE制作的备份的表。原有的表不会被覆盖;如果您试图覆盖一个原有的表,会发生错误。和BACKUP TABLE一样,RESTORE TABLE目前只对MyISAM表起作用。目录应被指定为一个完整路径名。 每个表的备份包括其.frm格式文件和.MYD数据文件。恢复操作会恢复这些文件,然后使用这些文件来重建.MYI索引文件。恢复操作比备份操作花的时间更长,这是因为需要重建索引。表含有的索引越多,花的时间就越长。 该语句会返回一个含有以下列的表: <table border="1" cellpadding="0" id="table13"><tr><td> <p><strong> <span>列</span></strong></p></td> <td> <p><strong> <span>值</span></strong></p></td> </tr><tr><td> <p> <span>Table</span></p></td> <td> <p>表名称</p></td> </tr><tr><td> <p> <span>Op</span></p></td> <td> <p><span>进行恢复</span></p></td> </tr><tr><td> <p> <span>Msg_type</span></p></td> <td> <p><span> 状态、错误、信息或警告之一</span></p></td> </tr><tr><td> <p> <span>Msg_text</span></p></td> <td> <p>消息</p></td> </tr></table> ### 13.5.3. SET语法 ~~~ SET variable_assignment [, variable_assignment] ... ~~~ ~~~   ~~~ ~~~ variable_assignment: ~~~ ~~~       user_var_name = expr ~~~ ~~~     | [GLOBAL | SESSION] system_var_name = expr ~~~ ~~~     | @@[global. | session.]system_var_name = expr ~~~ SET用于设置不同类型的变量。这些变量会影响服务器或客户端的操作。SET可以用于向用户变量或系统变量赋值。 用于分配账户密码的SET PASSWORD语句在[13.5.1.5节,“SET PASSWORD语法”](# "13.5.1.5. SET PASSWORD Syntax")中进行了讨论。 多数系统变量可以在运行时间被更改。可以被动态设置的系统变量在[5.3.3.1节,“动态系统变量”](# "5.3.3.1. Dynamic System Variables")中进行了讨论。 注释:旧版本的MySQL采用SET OPTION作为这个命令,但是由于有了SET,现在不赞成使用SET OPTION。 以下例子显示了您可以用于设置变量的不同语法。 用户变量可以被写作@*var_name*,并可以进行如下设置: ~~~ SET @var_name = expr; ~~~ 在[9.3节,“用户变量”](# "9.3. User Variables")中给出了有关用户变量的更多信息。 系统变量可以被作为*var_name*引用到SET语句中。在名称的前面可以自选地添加GLOBAL或@@global,以明确地指示该变量是全局变量。或者在名称前面添加SESSION, @@session,或@@,以指示它是一个会话变量。LOCAL和@@local是SESSION和@@session地同义词。如果没有修改符,则SET设置会话变量。 支持系统变量的@@*var_name*语法,以便使MySQL语法与其它数据库系统相兼容。 如果您在同一个语句中设置多个系统变量,则最后一个GLOBAL或SESSION选项被用于没有指定模式的变量。 ~~~ SET sort_buffer_size=10000; ~~~ ~~~ SET @@local.sort_buffer_size=10000; ~~~ ~~~ SET GLOBAL sort_buffer_size=1000000, SESSION sort_buffer_size=1000000; ~~~ ~~~ SET @@sort_buffer_size=1000000; ~~~ ~~~ SET @@global.sort_buffer_size=1000000, @@local.sort_buffer_size=1000000; ~~~ 如果您使用SESSION(默认情况)设置一个系统变量,则该值仍然有效,直到当前会话结束为止,或者直到您吧变量设置为一个不同的值为止。如果您使用GLOBAL(要求SUPER权限)来设置一个系统变量,则该值被记住,并被用于新的连接,直到服务器重新启动为止。如果您想要进行永久式变量设置,您应该把它放入一个选项文件。请参见[4.3.2节,“使用选项文件”](# "4.3.2. Using Option Files")。 为了防止不正确的使用,如果您使用SET GLOBAL时同时使用了一个只能与SET SESSION同时使用的变量,或者如果您在设置一个全局变量时未指定GLOBAL(或@@),则MySQL会产生一个错误。 如果您想要把一个SESSION变量设置为GLOBAL值或把一个GLOBAL值设置为内部MySQL默认值,需使用DEFAULT关键词。例如,在把max_join_size会话值设置为全局值时,以下两个语句是一样的: ~~~ SET max_join_size=DEFAULT; ~~~ ~~~ SET @@session.max_join_size=@@global.max_join_size; ~~~ 您可以使用SHOW VARIABLES来得到系统变量清单。(见[13.5.4.21节,“SHOW VARIABLES语法”](# "13.5.4.21. SHOW VARIABLES Syntax")。)要获得与样式匹配的一个具体的变量名称或者名称清单,需使用LIKE子句,使用方法如下: ~~~ SHOW VARIABLES LIKE 'max_join_size'; ~~~ ~~~ SHOW GLOBAL VARIABLES LIKE 'max_join_size'; ~~~ 要得到名称与样式匹配的变量的清单,需使用通配符‘%’: ~~~ SHOW VARIABLES LIKE 'have%'; ~~~ ~~~ SHOW GLOBAL VARIABLES LIKE 'have%'; ~~~ 通配符可以被用于相匹配的样式中的任何位置。 您也可以通过使用@@[global.|local.]var_name语法和SELECT来得到值: ~~~ SELECT @@max_join_size, @@global.max_join_size; ~~~ 当您使用SELECT @@var_name(即您不指定全局、会话或本地)来恢复一个变量时,则MySQL会返回SESSION值(如果存在)或者GLOBAL值。 以下清单用于描述带有非标准语法的变量,或描述在系统变量清单中(见[5.3.3节,“服务器系统变量”](# "5.3.3. Server System Variables")。)中没有描述的变量。尽管这些变量没有被SHOW VARIABLES显示,但是您可以使用SELECT来获得它们的值(例外情况是,使用CHARACTER SET和SET NAMES)。例如: ~~~ mysql> SELECT @@AUTOCOMMIT; ~~~ ~~~ +--------------+ ~~~ ~~~ | @@AUTOCOMMIT | ~~~ ~~~ +--------------+ ~~~ ~~~ |            1 | ~~~ ~~~ +--------------+ ~~~ ·         AUTOCOMMIT = {0 | 1} 设置autocommit模式。如果设置为1,则所有对表的更改会立刻生效。如果设置为0,则您必须使用COMMIT来接受一个事务,或使用ROLLBACK来取消它。如果您把AUTOCOMMIT模式从0改为1,则MySQL会对开放事务执行一个自动COMMIT。开始一个事务的另一种方法是使用一个START TRANSACTION或BEGIN语句。请参见[13.4.1节,“START TRANSACTION, COMMIT和ROLLBACK语法”](# "13.4.1. START TRANSACTION, COMMIT, and ROLLBACK Syntax")。 ·         BIG_TABLES = {0 | 1} 如果设置为1,所有的临时表被存储在磁盘中,而不是存储在储存期中。这样会稍微慢些,但是对于需要一个大型临时表的SELECT操作,不会发生The table *tbl_name* is full错误。对于一个新连接,默认值为0(使用存储器内部临时表)。通常,您不必设置此变量,因为根据需要,存储器内部表会被自动转换为以磁盘为基础的表。( 注释:本变量以前被命名为SQL_BIG_TABLES。) ·         CHARACTER SET {*charset_name* | DEFAULT} 本语句使用给定的映射为所有来自客户端和指向客户端的字符串建立映射。您可以通过在MySQL源分布中编辑sql/convert.cc来添加新的映射。SET CHARACTER SET用于设定三个会话系统变量:character_set_client和character_set_results被设置为给定的字符集,character_set_connection被设置为character_set_database值。 可以通过使用DEFAULT值恢复默认的映射。 注意,SET CHARACTER SET的语法与设置其它选项的语法不同。 ·         FOREIGN_KEY_CHECKS = {0 | 1} 如果设置为1(默认情况),则检查InnoDB表的外键限制条件。如果设置为0,则限制条件被忽略。如果重新载入InnoDB表时按照的顺序与上级/下级目录所要求的顺序不同,此时禁用外键检查是有用的。请参见[15.2.6.4节,“FOREIGN KEY约束”](# "15.2.6.4. FOREIGN KEY Constraints")。 ·         IDENTITY = *value* 该变量是LAST_INSERT_ID变量的同义词。该变量的作用是保持与其它数据库兼容。您可以使用SELECT @@IDENTITY读取其值,并可以使用SET IDENTITY设置它。 ·         INSERT_ID = *value* 用于设置将被以下INSERT或ALTER TABLE语句使用的值。此值在插入一个AUTO_INCREMENT值时使用。本语句主要和二进制日志同时使用。 ·         LAST_INSERT_ID = *value* 用于设定将从LAST_INSERT_ID()被返回的值。当您在用于更新表的语句中使用LAST_INSERT_ID()时,它被存储在二进制日志中。设置此变量不会更新由mysql_insert_id() C API函数返回的值。 ·         NAMES {'*charset_name*' | DEFAULT} SET NAMES用于把三个会话系统变量character_set_client, character_set_connection和character_set_results设置为给定的字符集。把character_set_connection设置为charset_name时,同时把collation_connection设置为charset_name的默认整序。 使用一个DEFAULT值可以恢复默认的映射。 注意,SET NAMES的语法与用于设置其它选项的语法不同。 ·         ONE_SHOT 这不是一个服务器系统变量,但是它可以被用来影响用于设置字符集、整序和时区的变量的效果。ONE_SHOT主要被用于复制:mysqlbinlog使用SET ONE_SHOT来暂时地修改字符集、整序和时区变量的值,以反映出它们原先的值。 您不能在使用ONE_SHOT时使用除允许的变量以外的变量;如果您这么做,您会得到如下错误: ~~~ mysql> SET ONE_SHOT max_allowed_packet = 1; ~~~ ~~~ ERROR 1382 (HY000): The 'SET ONE_SHOT' syntax is reserved for purposes internal to the MySQL server ~~~ 如果同时使用ONE_SHOT和被允许的变量,则会根据要求更改变量,但是会在下一个语句后,重新设置所有的字符集、整序和与时区有关的服务器系统变量。唯一的例外是,当下一个语句是SET语句时,不会进行重新设置。换句话说,在下一个非SET语句之后,才会进行重新设置。例如: ~~~ mysql> SET ONE_SHOT character_set_connection = latin5; ~~~ ~~~   ~~~ ~~~ mysql> SET ONE_SHOT collation_connection = latin5_turkish_ci; ~~~ ~~~   ~~~ ~~~ mysql> SHOW VARIABLES LIKE '%_connection'; ~~~ ~~~ +--------------------------+-------------------+ ~~~ ~~~ | Variable_name            | Value             | ~~~ ~~~ +--------------------------+-------------------+ ~~~ ~~~ | character_set_connection | latin5            | ~~~ ~~~ | collation_connection     | latin5_turkish_ci | ~~~ ~~~ +--------------------------+-------------------+ ~~~ ~~~   ~~~ ~~~ mysql> SHOW VARIABLES LIKE '%_connection'; ~~~ ~~~ +--------------------------+-------------------+ ~~~ ~~~ | Variable_name            | Value             | ~~~ ~~~ +--------------------------+-------------------+ ~~~ ~~~ | character_set_connection | latin1            | ~~~ ~~~ | collation_connection     | latin1_swedish_ci | ~~~ ~~~ +--------------------------+-------------------+ ~~~ ·         SQL_NOTES = {0 | 1} 当设置为1时(默认情况),“注意”一级的警报被记录下来。当设置为0时,“注意”警告被压制。Mysqldump包含输出,用于把此变量设置为0,这样,对于不会影响重新载入操作整体性的事件,重新载入转储文件时不会产生警告。 ·         SQL_AUTO_IS_NULL = {0 | 1} 如果设置为1(默认情况),您可以通过使用以下结构查找包含一个AUTO_INCREMENT列的表的最后插入的行: ~~~ WHERE auto_increment_column IS NULL ~~~ 此性质被有些ODBC程序,比如Access使用。 ·         SQL_BIG_SELECTS = {0 | 1} 如果设定为0,则MySQL会放弃有可能会花很长时间来执行的SELECT语句(也就是,对于这些语句,优化程序估算被检查的行的数目超过了max_join_size的值)。当一个不妥当的WHERE语句被发布后,本语句有用。一个新连接的默认值为1,这可以允许所有的SELECT语句。 如果您把max_join_size系统变量设置为除DEFAULT以外的值,则SQL_BIG_SELECTS被设置为0。 ·         SQL_BUFFER_RESULT = {0 | 1} SQL_BUFFER_RESULT会迫使来自SELECT语句的结果被放入临时表中。这可以帮助MySQL早点解除表锁定。当需要花较长时间把结果发送给客户端时,这是有好处的。 ·         SQL_LOG_BIN = {0 | 1} 如果设置为0,则客户端的二进制日志中不会记录日志。客户端必须拥有SUPER权限来设置此选项。 ·         SQL_LOG_OFF = {0 | 1} 如果设置为1,则此客户端的总查询日志中不会记录日志。客户端必须拥有SUPER权限来设置此选项。 ·         SQL_LOG_UPDATE = {0 | 1} 不赞成使用本变量。本变量被映射到SQL_LOG_BIN。 ·         SQL_QUOTE_SHOW_CREATE = {0 | 1} 如果设置为1,则SHOW CREATE TABLE会对表和列的名称加引号。如果设置为0,则加引号操作被禁用。默认情况下,本选项被启用,因此对于含有需要加引号的名称的表,复制操作起作用。请参见[13.5.4.5节,“SHOW CREATE TABLE语法”](# "13.5.4.5. SHOW CREATE TABLE Syntax")。 ·         SQL_SAFE_UPDATES = {0 | 1} 如果设置为1,则MySQL会放弃在WHERE子句或LIMIT子句中不使用关键字的UPDATE或DELETE语句。这样,当关键字使用不正确时,也有可能理解UPDATE或DELETE语句。这样就可以更改或删除大量的行。 ·         SQL_SELECT_LIMIT = {*value* | DEFAULT} 从SELECT语句返回的记录的最大数目。对于一个新连接,默认值是“unlimited”。如果您更改了限值,可以使用SQL_SELECT_LIMIT DEFAULT值恢复默认值。 如果SELECT有一个LIMIT子句,则LIMIT优先于SQL_SELECT_LIMIT值。 SQL_SELECT_LIMT不适用于在被存储的子程序中执行的SELECT语句。它也不适用于不会产生将被返回到客户端的结果集合的SELECT语句。这些包括子查询中的SELECT语句,CREATE TABLE...SELECT和INSERT INTO...SELECT。 ·         SQL_WARNINGS = {0 | 1} 本变量用于控制当出现警告时,单行INSERT语句是否产生一个信息字符串。默认值为0。把值设置为1,来产生一个信息字符串。 ·         TIMESTAMP = {*timestamp_value* | DEFAULT} 用于为此客户端设置时间。当您使用二进制日志来恢复行时,本语句用于得到原始的时间标记。timestamp_value应为一个Unix时间标记,而不是MySQL时间标记。 ·         UNIQUE_CHECKS = {0 | 1} 如果设置为1(默认情况),则会对InnoDB表中的二级索引执行唯一性检查。如果设置为0,则对于被插入到InnoDB的插入缓冲器中的索引登录项,不执行唯一性检查。如果您可以肯定您的数据不违反唯一性要求,则您可以把此值设定为0,以加快向InnoDB导入大型表的速度。 ### 13.5.4. SHOW语法 [13.5.4.1. SHOW CHARACTER SET语法](#)[13.5.4.2. SHOW COLLATION语法](#)[13.5.4.3. SHOW COLUMNS语法](#)[13.5.4.4. SHOW CREATE DATABASE语法](#)[13.5.4.5. SHOW CREATE TABLE语法](#)[13.5.4.6. SHOW DATABASES语法](#)[13.5.4.7. SHOW ENGINE语法](#)[13.5.4.8. SHOW ENGINES语法](#)[13.5.4.9. SHOW ERRORS语法](#)[13.5.4.10. SHOW GRANTS语法](#)[13.5.4.11. SHOW INDEX语法](#)[13.5.4.12. SHOW INNODB STATUS语法](#)[13.5.4.13. SHOW LOGS语法](#)[13.5.4.14. SHOW OPEN TABLES语法](#)[13.5.4.15. SHOW PRIVILEGES语法](#)[13.5.4.16. SHOW PROCESSLIST语法](#)[13.5.4.17. SHOW STATUS语法](#)[13.5.4.18. SHOW TABLE STATUS语法](#)[13.5.4.19. SHOW TABLES语法](#)[13.5.4.20. SHOW TRIGGERS语法](#)[13.5.4.21. SHOW VARIABLES语法](#)[13.5.4.22. SHOW WARNINGS语法](#) SHOW有多种形式,可以提供有关数据库、表、列或服务器状态的信息。本节叙述以下内容: ~~~ SHOW [FULL] COLUMNS FROM tbl_name [FROM db_name] [LIKE 'pattern'] ~~~ ~~~ SHOW CREATE DATABASE db_name ~~~ ~~~ SHOW CREATE TABLE tbl_name ~~~ ~~~ SHOW DATABASES [LIKE 'pattern'] ~~~ ~~~ SHOW ENGINE engine_name {LOGS | STATUS } ~~~ ~~~ SHOW [STORAGE] ENGINES ~~~ ~~~ SHOW ERRORS [LIMIT [offset,] row_count] ~~~ ~~~ SHOW GRANTS FOR user ~~~ ~~~ SHOW INDEX FROM tbl_name [FROM db_name] ~~~ ~~~ SHOW INNODB STATUS ~~~ ~~~ SHOW [BDB] LOGS ~~~ ~~~ SHOW PRIVILEGES ~~~ ~~~ SHOW [FULL] PROCESSLIST ~~~ ~~~ SHOW [GLOBAL | SESSION] STATUS [LIKE 'pattern'] ~~~ ~~~ SHOW TABLE STATUS [FROM db_name] [LIKE 'pattern'] ~~~ ~~~ SHOW [OPEN] TABLES [FROM db_name] [LIKE 'pattern'] ~~~ ~~~ SHOW TRIGGERS ~~~ ~~~ SHOW [GLOBAL | SESSION] VARIABLES [LIKE 'pattern'] ~~~ ~~~ SHOW WARNINGS [LIMIT [offset,] row_count] ~~~ SHOW语句还有一些形式,可以提供有关复制型主服务器和从属服务器的信息。这些形式在[13.6节,“复制语句”](# "13.6. Replication Statements")中进行了叙述。 ~~~ SHOW BINLOG EVENTS ~~~ ~~~ SHOW MASTER LOGS ~~~ ~~~ SHOW MASTER STATUS ~~~ ~~~ SHOW SLAVE HOSTS ~~~ ~~~ SHOW SLAVE STATUS ~~~ 如果一个给定的SHOW语句的语法包括一个LIKE '*pattern*'部分,则'*pattern*'是一个可以包含SQL ‘%’和‘_’通配符的字符串。对于把语句输出值限定为匹配值,本样式是有用的。 #### 13.5.4.1. SHOW CHARACTER SET语法 ~~~ SHOW CHARACTER SET [LIKE 'pattern'] ~~~ SHOW CHARACTER SET语句用于显示所有可用的字符集。该语句取一个自选的LIKE子句。该子句指示哪些字符集名称可以匹配。举例说明: ~~~ mysql> SHOW CHARACTER SET LIKE 'latin%'; ~~~ ~~~ +---------+-----------------------------+-------------------+--------+ ~~~ ~~~ | Charset | Description                 | Default collation | Maxlen | ~~~ ~~~ +---------+-----------------------------+-------------------+--------+ ~~~ ~~~ | latin1  | cp1252 West European        | latin1_swedish_ci |      1 | ~~~ ~~~ | latin2  | ISO 8859-2 Central European | latin2_general_ci |      1 | ~~~ ~~~ | latin5  | ISO 8859-9 Turkish          | latin5_turkish_ci |      1 | ~~~ ~~~ | latin7  | ISO 8859-13 Baltic          | latin7_general_ci |      1 | ~~~ ~~~ +---------+-----------------------------+-------------------+--------+ ~~~ Maxlen列显示用于存储一个字符的最大的字节数目。 #### 13.5.4.2. SHOW COLLATION语法 ~~~ SHOW COLLATION [LIKE 'pattern'] ~~~ 来自SHOW COLLATION的输出包括所有可用的字符集。该语句取一个自选的LIKE子句。该子句的pattern指示哪些整序名称可以匹配。举例说明: ~~~ mysql> SHOW COLLATION LIKE 'latin1%'; ~~~ ~~~ +-------------------+---------+----+---------+----------+---------+ ~~~ ~~~ | Collation         | Charset | Id | Default | Compiled | Sortlen | ~~~ ~~~ +-------------------+---------+----+---------+----------+---------+ ~~~ ~~~ | latin1_german1_ci | latin1  |  5 |         |          |       0 | ~~~ ~~~ | latin1_swedish_ci | latin1  |  8 | Yes     | Yes      |       0 | ~~~ ~~~ | latin1_danish_ci  | latin1  | 15 |         |          |       0 | ~~~ ~~~ | latin1_german2_ci | latin1  | 31 |         | Yes      |       2 | ~~~ ~~~ | latin1_bin        | latin1  | 47 |         | Yes      |       0 | ~~~ ~~~ | latin1_general_ci | latin1  | 48 |         |          |       0 | ~~~ ~~~ | latin1_general_cs | latin1  | 49 |         |          |       0 | ~~~ ~~~ | latin1_spanish_ci | latin1  | 94 |         |          |       0 | ~~~ ~~~ +-------------------+---------+----+---------+----------+---------+ ~~~ Default列指示对于其字符集,整序值是否是默认值。Compiled指示字符集是否被编辑到服务器中。Sortlen与对字符串(在字符集中表达)分类所需的存储器的数量有关。 #### 13.5.4.3. SHOW COLUMNS语法 ~~~ SHOW [FULL] COLUMNS FROM tbl_name [FROM db_name] [LIKE 'pattern'] ~~~ SHOW COLUMNS显示在一个给定表中的各列的信息。对于试图,本语句也起作用。 如果列类型与根据您的CREATE TABLE语句所预期的列类型不同,则需注意,当您创建或更改表时,MySQL有时会更改列类型。出现这种情况的条件在[13.1.5.1节,“沉寂的列规格变更”](# "13.1.5.1. Silent Column Specification Changes")中进行了描述。 FULL关键词会使得输出中包含您拥有的权限,并包含对每一列各自的评注。 您可以使用db_name.tbl_name作为tbl_name FROM db_name语法的另一种形式。换句话说,这两个语句是等价的: ~~~ mysql> SHOW COLUMNS FROM mytable FROM mydb; ~~~ ~~~ mysql> SHOW COLUMNS FROM mydb.mytable; ~~~ SHOW FIELDS是SHOW COLUMNS的同义词。您也可以使用mysqlshow db_name tbl_name命令列举表的各列。 DESCRIBE语句提供与SHOW COLUMNS相近的信息。请参见[13.3.1节,“DESCRIBE语法(获取关于列的信息)”](# "13.3.1. DESCRIBE Syntax (Get Information About Columns)")。 #### 13.5.4.4. SHOW CREATE DATABASE语法 ~~~ SHOW CREATE {DATABASE | SCHEMA} db_name ~~~ 显示用于创建给定数据库CREATE DATABASE语句。也可以使用SHOW CREATE SCHEMA。 ~~~ mysql> SHOW CREATE DATABASE test\G ~~~ ~~~ *************************** 1. row *************************** ~~~ ~~~        Database: test ~~~ ~~~ Create Database: CREATE DATABASE `test` ~~~ ~~~                  /*!40100 DEFAULT CHARACTER SET latin1 */ ~~~ ~~~   ~~~ ~~~ mysql> SHOW CREATE SCHEMA test\G ~~~ ~~~ *************************** 1. row *************************** ~~~ ~~~        Database: test ~~~ ~~~ Create Database: CREATE DATABASE `test` ~~~ ~~~                  /*!40100 DEFAULT CHARACTER SET latin1 */ ~~~ #### 13.5.4.5. SHOW CREATE TABLE语法 ~~~ SHOW CREATE TABLE tbl_name ~~~ 显示用于创建给定表的CREATE TABLE语句。本语句对视图也起作用。 ~~~ mysql> SHOW CREATE TABLE t\G ~~~ ~~~ *************************** 1. row *************************** ~~~ ~~~        Table: t ~~~ ~~~ Create Table: CREATE TABLE t ( ~~~ ~~~   id INT(11) default NULL auto_increment, ~~~ ~~~   s char(60) default NULL, ~~~ ~~~   PRIMARY KEY (id) ~~~ ~~~ ) ENGINE=MyISAM ~~~ ~~~   ~~~ 根据SQL_QUOTE_SHOW_CREATE选项,SHOW CREATE TABLE会对表名称和列名称加引号。请参见[13.5.3节,“SET语法”](# "13.5.3. SET Syntax")。 #### 13.5.4.6. SHOW DATABASES语法 ~~~ SHOW {DATABASES | SCHEMAS} [LIKE 'pattern'] ~~~ SHOW DATABASES可以在MySQL服务器主机上列举数据库。您也可以使用mysqlshow命令得到此清单。您只能看到您拥有某些权限的数据库,除非您拥有全局SHOW DATABASES权限。 如果服务器以--skip-show-database选项为起始,则您根本不能使用本语句,除非您拥有SHOW DATABASES权限。 也可以使用SHOW SCHEMAS。 #### 13.5.4.7. SHOW ENGINE语法 ~~~ SHOW ENGINE engine_name {LOGS | STATUS } ~~~ SHOW ENGINE显示存储引擎的日志或状态信息。目前支持以下语句: ~~~ SHOW ENGINE BDB LOGS ~~~ ~~~ SHOW ENGINE INNODB STATUS ~~~ SHOW ENGINE BDB LOGS显示原有BDB日志文件的状态信息。它会返回以下字段: ·         File 通向日志文件的完整路径。 ·         Type 日志文件类型(用于Berkeley DB日志文件的BDB)。 ·         Status 日志文件的状态(如果文件可以被取消,则为FREE。如果文件被事务子系统需要,则为IN USE) SHOW ENGINE INNODB STATUS显示InnoDB存储引擎状态的全面信息。 这些语句的旧的同义词(现在不赞成使用)是SHOW [BDB] LOGS和SHOW INNODB STATUS。 SHOW ENGINE可以从MySQL 4.1.2起使用。 #### 13.5.4.8. SHOW ENGINES语法 ~~~ SHOW [STORAGE] ENGINES ~~~ SHOW ENGINES显示存储引擎的状态信息。对于检查一个存储引擎是否被支持,或者对于查看默认引擎是什么,本语句十分有用。SHOW TABLE TYPES是同义词,但不赞成使用。 ~~~ mysql> SHOW ENGINES\G *************************** 1. row *************************** Engine: MyISAM Support: DEFAULT Comment: Default engine as of MySQL 3.23 with great performance *************************** 2. row *************************** Engine: MEMORY Support: YES Comment: Hash based, stored in memory, useful for temporary tables *************************** 3. row *************************** Engine: HEAP Support: YES Comment: Alias for MEMORY *************************** 4. row *************************** Engine: MERGE Support: YES Comment: Collection of identical MyISAM tables *************************** 5. row *************************** Engine: MRG_MYISAM Support: YES Comment: Alias for MERGE *************************** 6. row *************************** Engine: ISAM Support: NO Comment: Obsolete storage engine, now replaced by MyISAM *************************** 7. row *************************** Engine: MRG_ISAM Support: NO Comment: Obsolete storage engine, now replaced by MERGE *************************** 8. row *************************** Engine: InnoDB Support: YES Comment: Supports transactions, row-level locking, and foreign keys *************************** 9. row *************************** Engine: INNOBASE Support: YES Comment: Alias for INNODB *************************** 10. row *************************** Engine: BDB Support: NO Comment: Supports transactions and page-level locking *************************** 11. row *************************** Engine: BERKELEYDB Support: NO Comment: Alias for BDB *************************** 12. row *************************** Engine: NDBCLUSTER Support: DISABLED Comment: Clustered, fault-tolerant, memory-based tables *************************** 13. row *************************** Engine: NDB Support: DISABLED Comment: Alias for NDBCLUSTER *************************** 14. row *************************** Engine: EXAMPLE Support: NO Comment: Example storage engine *************************** 15. row *************************** Engine: ARCHIVE Support: YES Comment: Archive storage engine *************************** 16. row *************************** Engine: CSV Support: YES Comment: CSV storage engine *************************** 17. row *************************** Engine: FEDERATED Support: NO Comment: Federated MySQL storage engine *************************** 18. row *************************** Engine: BLACKHOLE Support: YES Comment: /dev/null storage engine (anything you write to it disappears) ~~~ Support值指示某个存储引擎是否被支持,并指示哪个是默认引擎。例如,如果服务器以--default-table-type=InnoDB选项为起始,则InnoDB行的Support值为DEFAULT值。请参见[第15章:*存储引擎和表类型*](# "Chapter 15. Storage Engines and Table Types")。 #### 13.5.4.9. SHOW ERRORS语法 ~~~ SHOW ERRORS [LIMIT [offset,] row_count] ~~~ ~~~ SHOW COUNT(*) ERRORS ~~~ 本语句与SHOW WARNINGS接近,不过该语句只显示错误,不同时显示错误、警告和注意。 LIMIT子句与SELECT语句具有相同的语法,请参见[13.2.7节,“SELECT语法”](# "13.2.7. SELECT Syntax")。 SHOW COUNT(*) ERRORS语句显示错误的数目。您也可以从error_count变量中找回此数目: ~~~ SHOW COUNT(*) ERRORS; ~~~ ~~~ SELECT @@error_count; ~~~ 要了解更多信息,请参见[13.5.4.22节,“SHOW WARNINGS语法”](# "13.5.4.22. SHOW WARNINGS Syntax")。 #### 13.5.4.10. SHOW GRANTS语法 ~~~ SHOW GRANTS FOR user ~~~ 本语句列出了在为MySQL用户账户复制权限时必须发布的GRANT语句。 ~~~ mysql> SHOW GRANTS FOR 'root'@'localhost'; ~~~ ~~~ +---------------------------------------------------------------------+ ~~~ ~~~ | Grants for root@localhost                                           | ~~~ ~~~ +---------------------------------------------------------------------+ ~~~ ~~~ | GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' WITH GRANT OPTION | ~~~ ~~~ +---------------------------------------------------------------------+ ~~~ 要对当前的会话列出权限,您可以使用以下语句之一: ~~~ SHOW GRANTS; ~~~ ~~~ SHOW GRANTS FOR CURRENT_USER; ~~~ ~~~ SHOW GRANTS FOR CURRENT_USER(); ~~~ #### 13.5.4.11. SHOW INDEX语法 ~~~ SHOW INDEX FROM tbl_name [FROM db_name] ~~~ SHOW INDEX会返回表索引信息。其格式与ODBC中的SQLStatistics调用相似。 SHOW INDEX会返回以下字段: ·         Table 表的名称。 ·         Non_unique 如果索引不能包括重复词,则为0。如果可以,则为1。 ·         Key_name 索引的名称。 ·         Seq_in_index 索引中的列序列号,从1开始。 ·         Column_name 列名称。 ·         Collation 列以什么方式存储在索引中。在MySQL中,有值‘A’(升序)或NULL(无分类)。 ·         Cardinality 索引中唯一值的数目的估计值。通过运行ANALYZE TABLE或**myisamchk -a**可以更新。基数根据被存储为整数的统计数据来计数,所以即使对于小型表,该值也没有必要是精确的。基数越大,当进行联合时,MySQL使用该索引的机会就越大。 ·         Sub_part 如果列只是被部分地编入索引,则为被编入索引的字符的数目。如果整列被编入索引,则为NULL。 ·         Packed 指示关键字如何被压缩。如果没有被压缩,则为NULL。 ·         Null 如果列含有NULL,则含有YES。如果没有,则该列含有NO。 ·         Index_type 用过的索引方法(BTREE, FULLTEXT, HASH, RTREE)。 ·         Comment 多种评注。 您可以使用db_name.tbl_name作为tbl_name FROM db_name语法的另一种形式。这两个语句是等价的: ~~~ mysql> SHOW INDEX FROM mytable FROM mydb; ~~~ ~~~ mysql> SHOW INDEX FROM mydb.mytable; ~~~ SHOW KEYS是SHOW INDEX的同义词。您也可以使用**mysqlshow -k *****db_name**********tbl_name***命令列举一个表的索引。 #### 13.5.4.12. SHOW INNODB STATUS语法 ~~~ SHOW INNODB STATUS ~~~ 在MySQL 5.1中,这是SHOW ENGINE INNODB STATUS的同义词,但不赞成使用。请参见[13.5.4.7节,“SHOW ENGINE语法”](# "13.5.4.7. SHOW ENGINE Syntax")。 #### 13.5.4.13. SHOW LOGS语法 ~~~ SHOW [BDB] LOGS ~~~ 在MySQL 5.1中,这是SHOW ENGINE BDB LOGS的同义词,但是不赞成使用。请参见[13.5.4.7节,“SHOW ENGINE语法”](# "13.5.4.7. SHOW ENGINE Syntax")。 #### 13.5.4.14. SHOW OPEN TABLES语法 ~~~ SHOW OPEN TABLES [FROM db_name] [LIKE 'pattern'] ~~~ SHOW OPEN TABLES列举在表缓存中当前被打开的非TEMPORARY表。请参见[7.4.9节,“MySQL如何打开和关闭表”](# "7.4.9. How MySQL Opens and Closes Tables")。 SHOW OPEN TABLES会返回以下字段: ·         Database 含有该表的数据库。 ·         Table 表名称。 ·         In_use 表当前被查询使用的次数。如果该数为零,则表是打开的,但是当前没有被使用。 ·         Name_locked 表名称是否被锁定。名称锁定用于取消表或对表进行重命名等操作。 #### 13.5.4.15. SHOW PRIVILEGES语法 ~~~ SHOW PRIVILEGES ~~~ SHOW PRIVILEGES显示MySQL服务器支持的系统权限清单。确切的输出根据您的服务器的版本而定。 ~~~ mysql> SHOW PRIVILEGES\G ~~~ ~~~ *************************** 1. row *************************** ~~~ ~~~ Privilege: Alter ~~~ ~~~ Context: Tables ~~~ ~~~ Comment: To alter the table ~~~ ~~~ *************************** 2. row *************************** ~~~ ~~~ Privilege: Alter routine ~~~ ~~~ Context: Functions,Procedures ~~~ ~~~ Comment: To alter or drop stored functions/procedures ~~~ ~~~ *************************** 3. row *************************** ~~~ ~~~ Privilege: Create ~~~ ~~~ Context: Databases,Tables,Indexes ~~~ ~~~ Comment: To create new databases and tables ~~~ ~~~ *************************** 4. row *************************** ~~~ ~~~ Privilege: Create routine ~~~ ~~~ Context: Functions,Procedures ~~~ ~~~ Comment: To use CREATE FUNCTION/PROCEDURE ~~~ ~~~ *************************** 5. row *************************** ~~~ ~~~ Privilege: Create temporary tables ~~~ ~~~ Context: Databases ~~~ ~~~ Comment: To use CREATE TEMPORARY TABLE ~~~ ~~~ *************************** 6. row *************************** ~~~ ~~~ Privilege: Create view ~~~ ~~~ Context: Tables ~~~ ~~~ Comment: To create new views ~~~ ~~~ *************************** 7. row *************************** ~~~ ~~~ Privilege: Create user ~~~ ~~~ Context: Server Admin ~~~ ~~~ Comment: To create new users ~~~ ~~~ *************************** 8. row *************************** ~~~ ~~~ Privilege: Delete ~~~ ~~~ Context: Tables ~~~ ~~~ Comment: To delete existing rows ~~~ ~~~ *************************** 9. row *************************** ~~~ ~~~ Privilege: Drop ~~~ ~~~ Context: Databases,Tables ~~~ ~~~ Comment: To drop databases, tables, and views ~~~ ~~~ *************************** 10. row *************************** ~~~ ~~~ Privilege: Execute ~~~ ~~~ Context: Functions,Procedures ~~~ ~~~ Comment: To execute stored routines ~~~ ~~~ *************************** 11. row *************************** ~~~ ~~~ Privilege: File ~~~ ~~~ Context: File access on server ~~~ ~~~ Comment: To read and write files on the server ~~~ ~~~ *************************** 12. row *************************** ~~~ ~~~ Privilege: Grant option ~~~ ~~~ Context: Databases,Tables,Functions,Procedures ~~~ ~~~ Comment: To give to other users those privileges you possess ~~~ ~~~ *************************** 13. row *************************** ~~~ ~~~ Privilege: Index ~~~ ~~~ Context: Tables ~~~ ~~~ Comment: To create or drop indexes ~~~ ~~~ *************************** 14. row *************************** ~~~ ~~~ Privilege: Insert ~~~ ~~~ Context: Tables ~~~ ~~~ Comment: To insert data into tables ~~~ ~~~ *************************** 15. row *************************** ~~~ ~~~ Privilege: Lock tables ~~~ ~~~ Context: Databases ~~~ ~~~ Comment: To use LOCK TABLES (together with SELECT privilege) ~~~ ~~~ *************************** 16. row *************************** ~~~ ~~~ Privilege: Process ~~~ ~~~ Context: Server Admin ~~~ ~~~ Comment: To view the plain text of currently executing queries ~~~ ~~~ *************************** 17. row *************************** ~~~ ~~~ Privilege: References ~~~ ~~~ Context: Databases,Tables ~~~ ~~~ Comment: To have references on tables ~~~ ~~~ *************************** 18. row *************************** ~~~ ~~~ Privilege: Reload ~~~ ~~~ Context: Server Admin ~~~ ~~~ Comment: To reload or refresh tables, logs and privileges ~~~ ~~~ *************************** 19. row *************************** ~~~ ~~~ Privilege: Replication client ~~~ ~~~ Context: Server Admin ~~~ ~~~ Comment: To ask where the slave or master servers are ~~~ ~~~ *************************** 20. row *************************** ~~~ ~~~ Privilege: Replication slave ~~~ ~~~ Context: Server Admin ~~~ ~~~ Comment: To read binary log events from the master ~~~ ~~~ *************************** 21. row *************************** ~~~ ~~~ Privilege: Select ~~~ ~~~ Context: Tables ~~~ ~~~ Comment: To retrieve rows from table ~~~ ~~~ *************************** 22. row *************************** ~~~ ~~~ Privilege: Show databases ~~~ ~~~ Context: Server Admin ~~~ ~~~ Comment: To see all databases with SHOW DATABASES ~~~ ~~~ *************************** 23. row *************************** ~~~ ~~~ Privilege: Show view ~~~ ~~~ Context: Tables ~~~ ~~~ Comment: To see views with SHOW CREATE VIEW ~~~ ~~~ *************************** 24. row *************************** ~~~ ~~~ Privilege: Shutdown ~~~ ~~~ Context: Server Admin ~~~ ~~~ Comment: To shut down the server ~~~ ~~~ *************************** 25. row *************************** ~~~ ~~~ Privilege: Super ~~~ ~~~ Context: Server Admin ~~~ ~~~ Comment: To use KILL thread, SET GLOBAL, CHANGE MASTER, etc. ~~~ ~~~ *************************** 26. row *************************** ~~~ ~~~ Privilege: Update ~~~ ~~~ Context: Tables ~~~ ~~~ Comment: To update existing rows ~~~ ~~~ *************************** 27. row *************************** ~~~ ~~~ Privilege: Usage ~~~ ~~~ Context: Server Admin ~~~ ~~~ Comment: No privileges - allow connect only ~~~ #### 13.5.4.16. SHOW PROCESSLIST语法 ~~~ SHOW [FULL] PROCESSLIST ~~~ SHOW PROCESSLIST显示哪些线程正在运行。您也可以使用**mysqladmin processlist**语句得到此信息。如果您有SUPER权限,您可以看到所有线程。否则,您只能看到您自己的线程(也就是,与您正在使用的MySQL账户相关的线程)。请参见[13.5.5.3节,“KILL语法”](# "13.5.5.3. KILL Syntax")。如果您不使用FULL关键词,则只显示每个查询的前100个字符。 本语句报告TCP/IP连接的主机名称(采用*host_name*:*client_port*格式),以方便地判定哪个客户端正在做什么。 如果您得到“too many connections”错误信息,并且想要了解正在发生的情况,本语句是非常有用的。MySQL保留一个额外的连接,让拥有SUPER权限的 账户使用,以确保管理员能够随时连接和检查系统(假设您没有把此权限给予所有的用户)。 在来自SHOW PROCESSLIST的输出中常见的一些状态: ·         Checking table 线程正在执行(自动)表格检查。 ·         Closing tables 意味着线程正在刷新更改后的表数据,并正在关闭使用过的表。这应该是一个快速的操作。如果不快,则您应该验证您的磁盘没有充满,并且磁盘没有被超负荷使用。 ·         Connect Out 连接到主服务器上的从属服务器。 ·         Copying to tmp table on disk 临时结果集合大于tmp_table_size。线程把临时表从存储器内部格式改变为磁盘模式,以节约存储器。 ·         Creating tmp table 线程正在创建一个临时表,以保持部分结果。 ·         deleting from main table 服务器正在执行多表删除的第一部分,只从第一个表中删除。 ·         deleting from reference tables 服务器正在执行多表删除的第二部分,从其它表中删除匹配的行。 ·         Flushing tables 线程正在执行FLUSH TABLES,并正在等待所有线程,以关闭表。 ·         FULLTEXT initialization 服务器正在准备执行一个自然语言全文本搜索。 ·         Killed 有人已经向线程发送了一个KILL命令。在下一次检查终止标记时,应放弃。该标记在MySQL的每个大循环中都检查,但是在有些情况下,线程终止只需要较短的时间。如果该线程被其它线程锁定,则只要其它线程接触锁定,终止操作就会生效。 ·         Locked 该查询被其它查询锁定。 ·         Sending data 线程正在为SELECT语句处理行,同时正在向客户端发送数据。 ·         Sorting for group 线程正在进行分类,以满足GROUP BY要求。 ·         Sorting for order 线程正在进行分类,以满足ORDER BY要求。 ·         Opening tables 线程正在试图打开一个表。这应该是非常快的过程,除非打开操作受到阻止。例如,一个ALTER TABLE或一个LOCK TABLE语句可以阻止打开一个表,直到语句完成为止。 ·         Removing duplicates 查询正在使用SELECT DISTINCT。使用时,在早期阶段,MySQL不能优化不同的操作。因此,MySQL要求一个额外的阶段,以便在把结果发送给客户端之前取消所有的复制行。 ·         Reopen table 线程得到一个表锁定,但是在得到锁定后被通知带下方的表结构已更改了。它已经释放了锁定,关闭了表,并试图重新打开它。 ·         Repair by sorting 修复代码正在使用一个分类来创建索引。 ·         Repair with keycache 修复代码正在通过关键缓存一个接一个地使用创建关键字。这比通过分类修复要慢很多。 ·         Searching rows for update 线程正在进行第一阶段,以在更新之前,查找所有匹配的行。如果UPDATE正在更改用于查找相关行的索引,则必须这么做。 ·         Sleeping 线程正在等待客户端,以向它发送一个新语句。 ·         System lock 线程正在等待得到一个用于表的外部系统锁定。如果您没有正在使用多个正在访问同一个表的mysqld服务器,则您可以使用--skip-external-locking选项禁用系统锁定。 ·         Upgrading lock INSERT DELAYED管理程序正在试图得到一个表锁定,以插入行。 ·         Updating 线程正在搜索行,并正在更新这些行。 ·         User Lock 线程正在等待GET_LOCK()。 ·         Waiting for tables 线程得到一个通知,表的底层结构已经改变,需要重新打开表以得到新的结构。但是,为了能重新打开表,必须等待,直到所有其它的线程已经关闭了正在被质询的表。 如果其它线程已经对正在被质询的表使用了FLUSH TABLES或以下语句之一:FLUSH TABLES*tbl_name*, ALTER TABLE, RENAME TABLE, REPAIR TABLE, ANALYZE TABLE或OPTIMIZE TABLE;则会出现通知。 ·         waiting for handler insert INSERT DELAYED管理程序已经处理了所有处于等待状态的插入,并正在等待新插入。 多数状态对应于非常快的操作。如果一个线程在这些状态下停留了数秒,则可能是有问题,需要进行调查。 有一些其它的状态,在前面的清单中没有提及,但是其中有很多状态对于查找服务器中的程序错误是有用的。 #### 13.5.4.17. SHOW STATUS语法 ~~~ SHOW [GLOBAL | SESSION] STATUS [LIKE 'pattern'] ~~~ SHOW STATUS提供服务器状态信息。此信息也可以使用**mysqladmin extended-status**命令获得。 此处显示了局部的输出。对于您的服务器,变量和值的清单可以是不同的。在[5.3.4节,“服务器状态变量”](# "5.3.4. Server Status Variables")中给出了每个变量的意义。 ~~~ mysql> SHOW STATUS; ~~~ ~~~ +--------------------------+------------+ ~~~ ~~~ | Variable_name            | Value      | ~~~ ~~~ +--------------------------+------------+ ~~~ ~~~ | Aborted_clients          | 0          | ~~~ ~~~ | Aborted_connects         | 0          | ~~~ ~~~ | Bytes_received           | 155372598  | ~~~ ~~~ | Bytes_sent               | 1176560426 | ~~~ ~~~ | Connections              | 30023      | ~~~ ~~~ | Created_tmp_disk_tables  | 0          | ~~~ ~~~ | Created_tmp_tables       | 8340       | ~~~ ~~~ | Created_tmp_files        | 60         | ~~~ ~~~ ...                       ...          ... ~~~ ~~~ | Open_tables              | 1          | ~~~ ~~~ | Open_files               | 2          | ~~~ ~~~ | Open_streams             | 0          | ~~~ ~~~ | Opened_tables            | 44600      | ~~~ ~~~ | Questions                | 2026873    | ~~~ ~~~ ...                       ...          ... ~~~ ~~~ | Table_locks_immediate    | 1920382    | ~~~ ~~~ | Table_locks_waited       | 0          | ~~~ ~~~ | Threads_cached           | 0          | ~~~ ~~~ | Threads_created          | 30022      | ~~~ ~~~ | Threads_connected        | 1          | ~~~ ~~~ | Threads_running          | 1          | ~~~ ~~~ | Uptime                   | 80380      | ~~~ ~~~ +--------------------------+------------+ ~~~ 使用LIKE子句,该语句只显示匹配该样式的那些变量: ~~~ mysql> SHOW STATUS LIKE 'Key%'; ~~~ ~~~ +--------------------+----------+ ~~~ ~~~ | Variable_name      | Value    | ~~~ ~~~ +--------------------+----------+ ~~~ ~~~ | Key_blocks_used    | 14955    | ~~~ ~~~ | Key_read_requests  | 96854827 | ~~~ ~~~ | Key_reads          | 162040   | ~~~ ~~~ | Key_write_requests | 7589728  | ~~~ ~~~ | Key_writes         | 3813196  | ~~~ ~~~ +--------------------+----------+ ~~~ 使用GLOBAL选项,您可以得到所有MySQL连接的状态值。使用SESSION,您可以得到所有当前连接的状态值。如果您两个选项都不使用,则默认值为SESSION。LOCAL是SESSION的同义词。 注意,有些状态变量只有一个全局值。对于这些变量,使用GLOBAL和SESSION会得到同样的值。 #### 13.5.4.18. SHOW TABLE STATUS语法 ~~~ SHOW TABLE STATUS [FROM db_name] [LIKE 'pattern'] ~~~ SHOW TABLE STATUS的性质与SHOW TABLE类似,不过,可以提供每个表的大量信息。您也可以使用**mysqlshow --status *****db_name***命令得到此清单。 本语句也显示视图信息。 对于NDB Cluster表,本语句的输出显示Avg_row_length和Data_length列的适当值,不过BLOB列没有被考虑进来。另外,复制数量在Comment列中显示(作为number_of_replicas)。 SHOW TABLE STATUS会返回以下字段: ·         Name 表的名称。 ·         Engine 表的存储引擎。在MySQL 4.1.2之前,本值被标记为Type。请参见[第15章:*存储引擎和表类型*](# "Chapter 15. Storage Engines and Table Types")。 ·         Version 表的.frm文件的版本号。 ·         Row_format 行存储格式(Fixed, Dynamic, Compressed, Redundant, Compact)。InnoDB表的格式被报告为Redundant或Compact。 ·         Rows 行的数目。部分存储引擎,如MyISAM,存储精确的数目。 对于其它存储引擎,比如InnoDB,本值是一个大约的数,与实际值相差可达40到50%。在这些情况下,使用SELECT COUNT(*)来获得准确的数目。 对于在INFORMATION_SCHEMA数据库中的表,Rows值为NULL。 ·         Avg_row_length 平均的行长度。 ·         Data_length 数据文件的长度。 ·         Max_data_length 数据文件的最大长度。如果给定了数据指针的大小,这是可以被存储在表中的数据的字节总数。 ·         Index_length 索引文件的长度。 ·         Data_free 被整序,但是未使用的字节的数目。 ·         Auto_increment 下一个AUTO_INCREMENT值。 ·         Create_time 什么时候表被创建。 ·         Update_time 什么时候数据文件被最后一次更新。 ·         Check_time 什么时候表被最后一次检查。不是所有的存储引擎此时都更新,在此情况下,值为NULL。 ·         Collation 表的字符集和整序。 ·         Checksum 活性校验和值。 ·         Create_options 和CREATE TABLE同时使用的额外选项。 ·         Comment 创建表时使用的评注(或者有关为什么MySQL可以访问表信息的说明)。 在表评注中,InnoDB表报告表所属的表空间的空闲空间。对于一个位于共享表空间中的表,这是共享表空间中的空闲空间。如果您正在使用多个表空间,并且该表有自己的表空间,则空闲空间只用于此表。 对于MEMORY (HEAP)表,Data_length, Max_data_length和Index_length值近似于被整序的存储器的实际值。整序算法预留了大量的存储器,以减少整序操作的数量。 对于视图,由SHOW TABLE STATUS显示的所有字段均为NULL。例外情况是Name指示为视图名称同时Comment称为视图。 #### 13.5.4.19. SHOW TABLES语法 ~~~ SHOW [FULL] TABLES [FROM db_name] [LIKE 'pattern'] ~~~ SHOW TABLES列举了给定数据库中的非TEMPORARY表。您也可以使用**mysqlshow *****db_name***命令得到此清单。 本命令也列举数据库中的其它视图。支持FULL修改符,这样SHOW FULL TABLES就可以显示第二个输出列。对于一个表,第二列的值为BASE TABLE;对于一个视图,第二列的值为VIEW。 注释:如果您对于一个表没有权限,则该表不会在来自SHOW TABLES或的**mysqlshow db_name**输出中显示。 #### 13.5.4.20. SHOW TRIGGERS语法 ~~~ SHOW TRIGGERS [FROM db_name] [LIKE expr] ~~~ SHOW TRIGGERS列出了目前被MySQL服务器定义的触发程序。 对于在[21.3节,“使用触发程序”](# "21.3. Using Triggers")中定义的触发程序ins_sum,本语句的输出显示如下: ~~~ mysql> SHOW TRIGGERS LIKE 'acc%'; ~~~ ~~~ +---------+--------+---------+-------------------------------+--------+---------+ ~~~ ~~~ | Trigger | Event  | Table   | Statement                     | Timing | Created | ~~~ ~~~ +---------+--------+---------+-------------------------------+--------+---------+ ~~~ ~~~ | ins_sum | INSERT | account |  SET @sum = @sum + NEW.amount | BEFORE | NULL    | ~~~ ~~~ +---------+--------+---------+-------------------------------+--------+---------+ ~~~ 注释:当使用一个含有SHOW TRIGGERS的LIKE子句时,待匹配的表达式(expr)会与触发程序定义时所在的表的名称相比较,而不与触发程序的名称相比较: ~~~ mysql> SHOW TRIGGERS LIKE 'ins%'; ~~~ ~~~ Empty set (0.01 sec) ~~~ 对本语句输出中的各列的简要解释如下: ·         Trigger: 触发程序的名称。 ·         Event: 调用触发程序的时间。必须为'INSERT', 'UPDATE'或 'DELETE'.之一。 ·         Table: 触发程序定义时对应的表。 ·         Statement: 当触发程序被调用时执行的语句。这与在INFORMATION_SCHEMA.TRIGGERS的ACTION_STATEMENT列中显示的文本一样。 ·         Timing: 'BEFORE'或'AFTER'两个值之一。 ·         Created: 目前,本列的值为NULL。 为了执行SHOW TRIGGERS,您必须拥有SUPER权限。 同时也见[23.1.16节,“INFORMATION_SCHEMA TRIGGERS表”](# "23.1.16. The INFORMATION_SCHEMA TRIGGERS Table")。 #### 13.5.4.21. SHOW VARIABLES语法 ~~~ SHOW [GLOBAL | SESSION] VARIABLES [LIKE 'pattern'] ~~~ SHOW VARIABLES显示了部门MySQL系统变量的值。本信息也可以使用**mysqladmin variables**命令获得。 使用GLOBAL选项,您可以获得被用于MySQL新连接的值。使用SESSION,您可以得到对于当前连接起效的值。如果您两个选项都不使用,默认值为SESSION。 LOCAL是SESSION的同义词。 如果默认值不合适,当mysqld启动时或在SET语句运行过程中,您可以使用命令行选项设置多数的这类变量。请参见[5.3.1节,“**mysqld**命令行选项”](# "5.3.1. mysqld Command-Line Options")和[13.5.3节,“SET语法](# "13.5.3. SET Syntax")。 此处显示了部分的输出。对于您的服务器,变量和值的清单会有所不同。在[5.3.3节,“服务器系统变量”](# "5.3.3. Server System Variables")中给出了每个变量的意义。在[7.5.2节,“调节服务器参数”](# "7.5.2. Tuning Server Parameters")中提供了有关调整变量的信息。 ~~~ mysql> SHOW VARIABLES; ~~~ ~~~ +---------------------------------+-----------------------------------------------+ ~~~ ~~~ | Variable_name                   | Value                                         | ~~~ ~~~ +---------------------------------+-----------------------------------------------+ ~~~ ~~~ | auto_increment_increment        | 1                                             | ~~~ ~~~ | auto_increment_offset           | 1                                             | ~~~ ~~~ | automatic_sp_privileges         | ON                                            | ~~~ ~~~ | back_log                        | 50                                            | ~~~ ~~~ | basedir                         | /home/jon/bin/mysql-5.1/                      | ~~~ ~~~ | binlog_cache_size               | 32768                                         | ~~~ ~~~ | bulk_insert_buffer_size         | 8388608                                       | ~~~ ~~~ | character_set_client            | latin1                                        | ~~~ ~~~ | character_set_connection        | latin1                                        | ~~~ ~~~ ...                               ...                                              ~~~ ~~~ | max_user_connections            | 0                                             | ~~~ ~~~ | max_write_lock_count            | 4294967295                                    | ~~~ ~~~ | multi_range_count               | 256                                           | ~~~ ~~~ | myisam_data_pointer_size        | 6                                             | ~~~ ~~~ | myisam_max_sort_file_size       | 2147483647                                    | ~~~ ~~~ | myisam_recover_options          | OFF                                           | ~~~ ~~~ | myisam_repair_threads           | 1                                             | ~~~ ~~~ | myisam_sort_buffer_size         | 8388608                                       | ~~~ ~~~ | ndb_autoincrement_prefetch_sz   | 32                                            | ~~~ ~~~ | ndb_cache_check_time            | 0                                             | ~~~ ~~~ | ndb_force_send                  | ON                                            | ~~~ ~~~ ...                               ...                                                       ...    ~~~ ~~~ | time_zone                       | SYSTEM                                        | ~~~ ~~~ | timed_mutexes                   | OFF                                           | ~~~ ~~~ | tmp_table_size                  | 33554432                                      | ~~~ ~~~ | tmpdir                          |                                               | ~~~ ~~~ | transaction_alloc_block_size    | 8192                                          | ~~~ ~~~ | transaction_prealloc_size       | 4096                                          | ~~~ ~~~ | tx_isolation                    | REPEATABLE-READ                               | ~~~ ~~~ | updatable_views_with_limit      | YES                                           | ~~~ ~~~ | version                         | 5.1.2-alpha-log                               | ~~~ ~~~ | version_comment                 | Source distribution                           | ~~~ ~~~ | version_compile_machine         | i686                                          | ~~~ ~~~ | version_compile_os              | suse-linux                                    | ~~~ ~~~ | wait_timeout                    | 28800                                         | ~~~ ~~~ +---------------------------------+-----------------------------------------------+ ~~~ 使用LIKE子句,本语句只显示与样式相匹配的变量: ~~~ mysql> SHOW VARIABLES LIKE 'have%'; ~~~ ~~~ +-----------------------+----------+ ~~~ ~~~ | Variable_name         | Value    | ~~~ ~~~ +-----------------------+----------+ ~~~ ~~~ | have_archive          | YES      | ~~~ ~~~ | have_bdb              | NO       | ~~~ ~~~ | have_blackhole_engine | YES      | ~~~ ~~~ | have_compress         | YES      | ~~~ ~~~ | have_crypt            | YES      | ~~~ ~~~ | have_csv              | YES      | ~~~ ~~~ | have_example_engine   | NO       | ~~~ ~~~ | have_federated_engine | NO       | ~~~ ~~~ | have_geometry         | YES      | ~~~ ~~~ | have_innodb           | YES      | ~~~ ~~~ | have_isam             | NO       | ~~~ ~~~ | have_ndbcluster       | DISABLED | ~~~ ~~~ | have_openssl          | NO       | ~~~ ~~~ | have_partition_engine | YES      | ~~~ ~~~ | have_query_cache      | YES      | ~~~ ~~~ | have_raid             | NO       | ~~~ ~~~ | have_rtree_keys       | YES      | ~~~ ~~~ | have_symlink          | YES      | ~~~ ~~~ +-----------------------+----------+ ~~~ #### 13.5.4.22. SHOW WARNINGS语法 ~~~ SHOW WARNINGS [LIMIT [offset,] row_count] ~~~ ~~~ SHOW COUNT(*) WARNINGS ~~~ SHOW WARNINGS显示由上一个生成消息的语句导致的错误、警告和注意消息。如果上一个使用表的语句未生成消息,则什么也不显示。SHOW ERRORS是其相关语句,只显示错误。请参见[13.5.4.9节,“SHOW ERRORS语法”](# "13.5.4.9. SHOW ERRORS Syntax")。 对于使用一个表的每个新语句,消息清单均重新设置。 SHOW COUNT(*) WARNINGS语句显示错误、警告和注意的总数。您也可以从warning_count变量中找回此数目。 ~~~ SHOW COUNT(*) WARNINGS; ~~~ ~~~ SELECT @@warning_count; ~~~ 如果max_error_count系统变量设置得过低,以致于有的消息没有被存储,则warning_count值可能比由SHOW WARNINGS显示的消息数目要大。本节后部显示的例子展示了这类情况是如何发生的。 LIMIT子句具有与SELECT语句相同的语法。请参见[13.2.7节,“SELECT语法”](# "13.2.7. SELECT Syntax")。 MySQL服务器会发回由上一个语句引起的错误、警告和注意的总数。如果您正在使用C API,则此值可以通过调用mysql_warning_count()来获得。请参见[25.2.3.69节,“mysql_warning_count()”](# "25.2.3.69. mysql_warning_count()")。 对于如LOAD DATA INFILE等语句和如INSERT, UPDATE, CREATE TABLE和ALTER TABLE等DML语句,会生成警告。 以下DROP TABLE语句会导致一个注意: ~~~ mysql> DROP TABLE IF EXISTS no_such_table; ~~~ ~~~ mysql> SHOW WARNINGS; ~~~ ~~~ +-------+------+-------------------------------+ ~~~ ~~~ | Level | Code | Message                       | ~~~ ~~~ +-------+------+-------------------------------+ ~~~ ~~~ | Note  | 1051 | Unknown table 'no_such_table' | ~~~ ~~~ +-------+------+-------------------------------+ ~~~ 以下是一个简单的例子,显示了对于CREATE TABLE的一个语法警告,和对于INSERT的转换警告: ~~~ mysql> CREATE TABLE t1 (a TINYINT NOT NULL, b CHAR(4)) TYPE=MyISAM; ~~~ ~~~ Query OK, 0 rows affected, 1 warning (0.00 sec) ~~~ ~~~ mysql> SHOW WARNINGS\G ~~~ ~~~ *************************** 1. row *************************** ~~~ ~~~   Level: Warning ~~~ ~~~    Code: 1287 ~~~ ~~~ Message: 'TYPE=storage_engine' is deprecated, use ~~~ ~~~          'ENGINE=storage_engine' instead ~~~ ~~~ 1 row in set (0.00 sec) ~~~ ~~~   ~~~ ~~~ mysql> INSERT INTO t1 VALUES(10,'mysql'),(NULL,'test'), ~~~ ~~~     -> (300,'Open Source'); ~~~ ~~~ Query OK, 3 rows affected, 4 warnings (0.01 sec) ~~~ ~~~ Records: 3  Duplicates: 0  Warnings: 4 ~~~ ~~~   ~~~ ~~~ mysql> SHOW WARNINGS\G ~~~ ~~~ *************************** 1. row *************************** ~~~ ~~~   Level: Warning ~~~ ~~~    Code: 1265 ~~~ ~~~ Message: Data truncated for column 'b' at row 1 ~~~ ~~~ *************************** 2. row *************************** ~~~ ~~~   Level: Warning ~~~ ~~~    Code: 1263 ~~~ ~~~ Message: Data truncated, NULL supplied to NOT NULL column 'a' at row 2 ~~~ ~~~ *************************** 3. row *************************** ~~~ ~~~   Level: Warning ~~~ ~~~    Code: 1264 ~~~ ~~~ Message: Data truncated, out of range for column 'a' at row 3 ~~~ ~~~ *************************** 4. row *************************** ~~~ ~~~   Level: Warning ~~~ ~~~    Code: 1265 ~~~ ~~~ Message: Data truncated for column 'b' at row 3 ~~~ ~~~ 4 rows in set (0.00 sec) ~~~ 要存储的错误、警告和注意消息的最大数目由max_error_count系统变量控制。默认情况下,该值为64。要更改您想要存储的信息的数目,需更改max_error_count值。在下面的例子中,ALTER TABLE语句会产生三个警告消息,但是只有一个被存储,因为max_error_count被设置为1: ~~~ mysql> SHOW VARIABLES LIKE 'max_error_count'; ~~~ ~~~ +-----------------+-------+ ~~~ ~~~ | Variable_name   | Value | ~~~ ~~~ +-----------------+-------+ ~~~ ~~~ | max_error_count | 64    | ~~~ ~~~ +-----------------+-------+ ~~~ ~~~ 1 row in set (0.00 sec) ~~~ ~~~   ~~~ ~~~ mysql> SET max_error_count=1; ~~~ ~~~ Query OK, 0 rows affected (0.00 sec) ~~~ ~~~   ~~~ ~~~ mysql> ALTER TABLE t1 MODIFY b CHAR; ~~~ ~~~ Query OK, 3 rows affected, 3 warnings (0.00 sec) ~~~ ~~~ Records: 3  Duplicates: 0  Warnings: 3 ~~~ ~~~   ~~~ ~~~ mysql> SELECT @@warning_count; ~~~ ~~~ +-----------------+ ~~~ ~~~ | @@warning_count | ~~~ ~~~ +-----------------+ ~~~ ~~~ |               3 | ~~~ ~~~ +-----------------+ ~~~ ~~~ 1 row in set (0.01 sec) ~~~ ~~~   ~~~ ~~~ mysql> SHOW WARNINGS; ~~~ ~~~ +---------+------+----------------------------------------+ ~~~ ~~~ | Level   | Code | Message                                | ~~~ ~~~ +---------+------+----------------------------------------+ ~~~ ~~~ | Warning | 1263 | Data truncated for column 'b' at row 1 | ~~~ ~~~ +---------+------+----------------------------------------+ ~~~ ~~~ 1 row in set (0.00 sec) ~~~ 要禁用警告,需把max_error_count设置为0。在此情况下,warning_count仍然指示有多少警告已经发生,但是这些消息不被存储。 您可以把SQL_NOTES会话变量设置为0,使“注意”级别的警告不被记录。 ### 13.5.5. 其它管理语句 [13.5.5.1. CACHE INDEX语法](#)[13.5.5.2. FLUSH语法](#)[13.5.5.3. KILL语法](#)[13.5.5.4. LOAD INDEX INTO CACHE语法](#)[13.5.5.5. RESET语法](#) #### 13.5.5.1. CACHE INDEX语法 ~~~ CACHE INDEX ~~~ ~~~   tbl_index_list [, tbl_index_list] ... ~~~ ~~~   IN key_cache_name ~~~ ~~~   ~~~ ~~~ tbl_index_list: ~~~ ~~~   tbl_name [[INDEX|KEY] (index_name[, index_name] ...)] ~~~ CACHE INDEX语句把表索引分配给某个关键缓存。该语句只用于MyISAM表。 下列语句把索引从表t1, t2和t3分配到名为hot_cache的关键缓存: ~~~ mysql> CACHE INDEX t1, t2, t3 IN hot_cache; ~~~ ~~~ +---------+--------------------+----------+----------+ ~~~ ~~~ | Table   | Op                 | Msg_type | Msg_text | ~~~ ~~~ +---------+--------------------+----------+----------+ ~~~ ~~~ | test.t1 | assign_to_keycache | status   | OK       | ~~~ ~~~ | test.t2 | assign_to_keycache | status   | OK       | ~~~ ~~~ | test.t3 | assign_to_keycache | status   | OK       | ~~~ ~~~ +---------+--------------------+----------+----------+ ~~~ CACHE INDEX语法允许您指定,只有来自表的特定索引应被分配给缓存。但是,当前的实施会把所有的表索引分配给缓存,所以必须指定表名称,不能指定其它的。 被引用到CACHE INDEX语句中的关键缓存可以这样创建,即通过使用一个参数设置语句或在服务器参数设置中设置其大小。举例说明: ~~~ mysql> SET GLOBAL keycache1.key_buffer_size=128*1024; ~~~ 关键缓存参数可以被作为一个结构化系统变量的成分进行访问。请参见[9.4.1节,“结构式系统变量”](# "9.4.1. Structured System Variables")。 在您可以把索引分配给一个关键缓存以前,缓存必须存在: ~~~ mysql> CACHE INDEX t1 IN non_existent_cache; ~~~ ~~~ ERROR 1284 (HY000): Unknown key cache 'non_existent_cache' ~~~ 默认情况下,表索引被分配给在服务器启动时被创建的主(默认)键缓存。当一个键高速缓冲被破坏时,所有被分配到此缓存中的索引会再次被分配给默认的 键高速缓冲。 索引的分配会对服务器产生全局性影响:如果一个客户端把一个索引分配给一个给定的缓存,则不论什么客户端发布查询,本缓存都被用于所有涉及索引的查询。 #### 13.5.5.2. FLUSH语法 ~~~ FLUSH [LOCAL | NO_WRITE_TO_BINLOG] flush_option [, flush_option] ... ~~~ 如果您想要清除MySQL使用的部分内部缓存,您应该使用FLUSH语句。要执行FLUSH,您必须拥有RELOAD权限。 flush_option可以为以下的任何一个: ·         HOSTS 用于清空主机缓存表。如果有的主机更改了IP号或如果您得到了错误信息Host *host_name* is blocked,则您应该刷新主机表。当在连接到MySQL服务器时,如果对于一个给定的主机,接连出现错误“多于max_connect_errors”,此时,MySQL会假定出现了错误,并阻止主机后续的连接申请。刷新主机表允许主机尝试再次连接。请参见[A.2.5节,“主机的hos*t_name被屏蔽*”](# "A.2.5. Host 'host_name' is blocked")。您可以使用max_connect_errors=999999999启动**mysqld**,以避免此错误信息。 ·         DES_KEY_FILE 用于在服务器启动时,从采用--des-key-file选项指定的文件重新载入DES关键字。 ·         LOGS 用于关闭并重新打开所有的日志文件。如果您已经指定了一个更新日志文件或一个二进制日志文件,同时没有扩展,则相对于前一个文件,日志文件的扩展号增加1。如果您在文件名称中使用了一个扩展,则MySQL会关闭并重新打开日志文件。在Unix中,当相mysqld服务器发送一个SIGHUP信号时,也会如此(例外情况是部分Mac OS X 10.3版本。在这些版本中,mysqld忽略SIGHUP和SIGQUIT)。 如果服务器使用--log-error选项,则FLUSH LOGS会导致错误日志被重命名(使用后缀-old),同时mysqld会创建一个新的空日志文件。如果没有给定--log-error选项,则不会进行重命名。 ·         PRIVILEGES 用于从mysql数据库中的授权表重新载入权限。 ·         QUERY CACHE 对查询缓存进行整理碎片,以更好得利用存储器。与RESET QUERY CACHE不同,本语句不会从缓存中取消任何查询。 ·         STATUS 用于把多数状态变量重新设置为零。只在当调试查询时,您才应该使用此项。请参见[1.7.1.3节,“如何通报缺陷和问题”](# "1.7.1.3. How to Report Bugs or Problems")。 ·         {TABLE | TABLES} [*tbl_name* [, *tbl_name*] ...] 当没有表被命名时,关闭所有打开的表,并迫使所有正在使用的表关闭。这也会刷新查询缓存。此项含有一个或多个表名称,只刷新给定的表。和RESET QUERY CACHE语句一样,FLUSH TABLES还会取消来自查询缓存的所有查询结果。 ·         TABLES WITH READ LOCK 对于所有带读取锁定的数据库,关闭所有打开的表,并锁定所有的表,直到您执行UNLOCK TABLES为止。如果您拥有一个可以及时进行快照的文件系统,比如Veritas,则这是进行备份的非常方便的方法。 ·         USER_RESOURCES 用于把所有每小时用户资源重新设置为零。这可以使已经达到了每小时连接、查询或更新限值的客户端立刻重新恢复活性。FLUSH USER_RESOURCES不适用于同时连接的最大限值。请参见[13.5.1.3节,“GRANT和REVOKE语法”](# "13.5.1.3. GRANT and REVOKE Syntax")。 FLUSH语句被写入二进制日志,除非使用了自选的NO_WRITE_TO_BINLOG关键字(或其别名LOCAL)。 注释:在任何情况下,FLUSH LOGS, FLUSH MASTER, FLUSH SLAVE和FLUSH TABLES WITH READ LOCK都不会被记入日志,因为如果它们被复制到一个从属服务器上,会导致出现问题。 您也可以使用flush-hosts, flush-logs, flush-privileges, flush-status或flush-tables命令访问含有mysqladmin应用程序的语句。 注释:在MySQL 5.1.2-alpha中,不可能在已存储的函数或触发程序中发布FLUSH语句。不过,您可以在已存储的过程中使用FLUSH,只要它们不会从已存储的函数或触发程序中被调用。请参见[I.1节,“对存储子程序和触发程序的限制”](# "I.1. Restrictions on Stored Routines and Triggers")。 要了解有关RESET语句与复制同时使用的信息,也可以见[13.5.5.5节,“RESET语法”](# "13.5.5.5. RESET Syntax")。 #### 13.5.5.3. KILL语法 ~~~ KILL [CONNECTION | QUERY] thread_id ~~~ 每个与mysqld的连接都在一个独立的线程里运行,您可以使用SHOW PROCESSLIST语句查看哪些线程正在运行,并使用KILL *thread_id*语句终止一个线程。 KILL允许自选的CONNECTION或QUERY修改符: ·         KILL CONNECTION与不含修改符的KILL一样:它会终止与给定的*thread_id*有关的连接。 ·         KILL QUERY会终止连接当前正在执行的语句,但是会保持连接的原状。 如果您拥有PROCESS权限,则您可以查看所有线程。如果您拥有SUPER权限,您可以终止所有线程和语句。否则,您只能查看和终止您自己的线程和语句。 您也可以使用**mysqladmin processlist**和**mysqladmin kill**命令来检查和终止线程。 注释:您不能同时使用KILL和Embedded MySQL Server库,因为内植的服务器只运行主机应用程序的线程。它不能创建任何自身的连接线程。 当您进行一个KILL时,对线程设置一个特有的终止标记。在多数情况下,线程终止可能要花一些时间,这是因为终止标记只会在在特定的间隔被检查: ·         在SELECT, ORDER BY和GROUP BY循环中,在读取一组行后检查标记。如果设置了终止标记,则该语句被放弃。 ·         在ALTER TABLE过程中,在每组行从原来的表中被读取前,检查终止标记。如果设置了终止标记,则语句被放弃,临时表被删除。 ·         在UPDATE或DELETE运行期间,在每个组读取之后以及每个已更行或已删除的行之后,检查终止标记。如果终止标记被设置,则该语句被放弃。注意,如果您正在使用事务,则变更不会被 回滚。 ·         GET_LOCK()会放弃和返回NULL。 ·         INSERT DELAYED线程会快速地刷新(插入)它在存储器中的所有的行,然后终止。 ·         如果线程在表锁定管理程序中(状态:锁定),则表锁定被快速地放弃。 ·         如果在写入调用中,线程正在等待空闲的磁盘空间,则写入被放弃,并伴随"disk full"错误消息。 ·         警告:对MyISAM表终止一个REPAIR TABLE或OPTIMIZE TABLE操作会导致出现一个被损坏的没有用的表。对这样的表的任何读取或写入都会失败,直到您再次优化或修复它(不中断)。 #### 13.5.5.4. LOAD INDEX INTO CACHE语法 ~~~ LOAD INDEX INTO CACHE ~~~ ~~~   tbl_index_list [, tbl_index_list] ... ~~~ ~~~   ~~~ ~~~ tbl_index_list: ~~~ ~~~   tbl_name ~~~ ~~~     [[INDEX|KEY] (index_name[, index_name] ...)] ~~~ ~~~     [IGNORE LEAVES] ~~~ LOAD INDEX INTO CACHE语句会把一个表索引预载入到某个关键缓存中。它已经被一个明确的CACHE INDEX语句分配到此关键缓存中。或者,表索引被预载入到默认的关键缓存中。LOAD INDEX INTO CACHE只用于MyISAM表。 IGNORE LEAVES修改符只会导致索引的非叶子节点被预载入。 对于表t1和t2,以下语句会预载入索引的节点(索引组): ~~~ mysql> LOAD INDEX INTO CACHE t1, t2 IGNORE LEAVES; ~~~ ~~~ +---------+--------------+----------+----------+ ~~~ ~~~ | Table   | Op           | Msg_type | Msg_text | ~~~ ~~~ +---------+--------------+----------+----------+ ~~~ ~~~ | test.t1 | preload_keys | status   | OK       | ~~~ ~~~ | test.t2 | preload_keys | status   | OK       | ~~~ ~~~ +---------+--------------+----------+----------+ ~~~ 本语句会预载入所有来自t1的索引组。它只预载入来自t2的非叶子节点的组。 LOAD INDEX INTO CACHE语法允许您指定,只有来自表的特定的索引应被预载入。但是,当前实施会把所有的表索引预载入缓存中,所以一定要指定表名称,不能指定其它的。 #### 13.5.5.5. RESET语法 ~~~ RESET reset_option [, reset_option] ... ~~~ RESET语句被用于清除不同的服务器操作的状态。它也作为FLUSH语句的更强大的版本。请参见[13.5.5.2节,“FLUSH语法”](# "13.5.5.2. FLUSH Syntax")。 为了执行RESET,您必须拥有RELOAD权限。 reset_option可以为以下的任何一项: ·         MASTER 可以删除列于索引文件中的所有二进制日志,把二进制日志索引文件重新设置为空,并创建一个新的二进制日志文件。(在以前版本的MySQL中,被称为FLUSH MASTER。)见[13.6.1节,“用于控制主服务器的SQL语句”](# "13.6.1. SQL Statements for Controlling Master Servers")。 ·         QUERY CACHE 从查询缓存中取消所有的查询结果。 ·         SLAVE 可以使从属服务器忘记其在主服务器二进制日志中的复制位置,另外,也可以通过删除原有的中继日志文件和开始一个新文件来重新设置中继日志。请参见[13.6.2节,“用于控制从服务器的SQL语句”](# "13.6.2. SQL Statements for Controlling Slave Servers")。 ### 13.6. 复制语句 [ 13.6.1. 用于控制主服务器的SQL语句](#)[ 13.6.2. 用于控制从服务器的SQL语句](#) 本节叙述了与复制有关的SQL语句。一组语句被用于控制主服务器。其它的被用于控制从属服务器。 ### 13.6.1. 用于控制主服务器的SQL语句 [13.6.1.1. PURGE MASTER LOGS语法](#)[13.6.1.2. RESET MASTER语法](#)[13.6.1.3. SET SQL_LOG_BIN语法](#)[13.6.1.4. SHOW BINLOG EVENTS语法](#)[13.6.1.5. SHOW MASTER LOGS语法](#)[13.6.1.6. SHOW MASTER STATUS语法](#)[13.6.1.7. SHOW SLAVE HOSTS语法](#) 可以通过SQL界面控制复制。本节讨论了用于管理主复制服务器的语句。[13.6.2节,“用于控制从服务器的SQL语句”](# "13.6.2. SQL Statements for Controlling Slave Servers")讨论了用于管理从属服务器的语句。 #### 13.6.1.1. PURGE MASTER LOGS语法 ~~~ PURGE {MASTER | BINARY} LOGS TO 'log_name' ~~~ ~~~ PURGE {MASTER | BINARY} LOGS BEFORE 'date' ~~~ 用于删除列于在指定的日志或日期之前的日志索引中的所有二进制日志。这些日志也会从记录在日志索引文件中的清单中被删除,这样被给定的日志成为第一个。 例如: ~~~ PURGE MASTER LOGS TO 'mysql-bin.010'; ~~~ ~~~ PURGE MASTER LOGS BEFORE '2003-04-02 22:46:26'; ~~~ BEFORE变量的date自变量可以为'YYYY-MM-DD hh:mm:ss'格式。MASTER和BINARY是同义词。 如果您有一个活性的从属服务器,该服务器当前正在读取您正在试图删除的日志之一,则本语句不会起作用,而是会失败,并伴随一个错误。不过,如果从属服务器是休止的,并且您碰巧清理了其想要读取的日志之一,则从属服务器启动后不能复制。当从属服务器正在复制时,本语句可以安全运行。您不需要停止它们。 要清理日志,需按照以下步骤: 1.    在每个从属服务器上,使用SHOW SLAVE STATUS来检查它正在读取哪个日志。 2.    使用SHOW MASTER LOGS获得主服务器上的一系列日志。 3.    在所有的从属服务器中判定最早的日志。这个是目标日志。如果所有的从属服务器是更新的,这是清单上的最后一个日志。 4.    制作您将要删除的所有日志的备份。(这个步骤是自选的,但是建议采用。) 5.    清理所有的日志,但是不包括目标日志。 #### 13.6.1.2. RESET MASTER语法 ~~~ RESET MASTER ~~~ 可以删除列于索引文件中的所有二进制日志,把二进制日志索引文件重新设置为空,并创建一个新的二进制日志文件。 #### 13.6.1.3. SET SQL_LOG_BIN语法 ~~~ SET SQL_LOG_BIN = {0|1} ~~~ 如果客户端使用一个有SUPER权限的账户连接,则可以禁用或启用当前连接的二进制日志记录。如果客户端没有此权限,则语句被拒绝,并伴随有错误。 #### 13.6.1.4. SHOW BINLOG EVENTS语法 ~~~ SHOW BINLOG EVENTS ~~~ ~~~    [IN 'log_name'] [FROM pos] [LIMIT [offset,] row_count] ~~~ 用于在二进制日志中显示事件。如果您不指定'log_name',则显示第一个二进制日志。 LIMIT子句和SELECT语句具有相同的语法。请参见[13.2.7节,“SELECT语法”](# "13.2.7. SELECT Syntax")。 注释:当服务器把二进制日志的完整内容(该日志包括多数的由MySQL执行的查询)转储到stdout时,发布一个不含LIMIT子句的SHOW BINLOG EVENTS可以启动一个过程,该过程非常消耗时间并消耗资源。要把二进制日志保存到一个文本文件中,用于以后的检查和分析,需使用mysqlbinlog应用程序。请参见[8.6节,“mysqlbinlog:用于处理二进制日志文件的实用工具”](# "8.6. mysqlbinlog — Utility for Processing Binary Log Files")。 #### 13.6.1.5. SHOW MASTER LOGS语法 ~~~ SHOW MASTER LOGS ~~~ ~~~ SHOW BINARY LOGS ~~~ 用于列出服务器中的二进制日志文件。本语句被用作[13.6.1.1节,“PURGE MASTER LOGS语法”](# "13.6.1.1. PURGE MASTER LOGS Syntax")中所述的过程的一部分,用于确定哪些日志可以被清理。 ~~~ mysql> SHOW BINARY LOGS; ~~~ ~~~ +---------------+-----------+ ~~~ ~~~ | Log_name      | File_size | ~~~ ~~~ +---------------+-----------+ ~~~ ~~~ | binlog.000015 |    724935 | ~~~ ~~~ | binlog.000016 |    733481 | ~~~ ~~~ +---------------+-----------+ ~~~ SHOW BINARY LOGS与SHOW MASTER LOGS相当。 #### 13.6.1.6. SHOW MASTER STATUS语法 ~~~ SHOW MASTER STATUS ~~~ 用于提供主服务器二进制日志文件的状态信息。例如: ~~~ mysql > SHOW MASTER STATUS; ~~~ ~~~ +---------------+----------+--------------+------------------+ ~~~ ~~~ | File          | Position | Binlog_Do_DB | Binlog_Ignore_DB | ~~~ ~~~ +---------------+----------+--------------+------------------+ ~~~ ~~~ | mysql-bin.003 | 73       | test         | manual,mysql     | ~~~ ~~~ +---------------+----------+--------------+------------------+ ~~~ #### 13.6.1.7. SHOW SLAVE HOSTS语法 ~~~ SHOW SLAVE HOSTS ~~~ 用于显示当前使用主服务器注册的复制从属服务器的清单。不以--report-host=*slave_name*选项为开头的从属服务器不会显示在本清单中。 ### 13.6.2. 用于控制从服务器的SQL语句 [13.6.2.1. CHANGE MASTER TO语法](#)[13.6.2.2. LOAD DATA FROM MASTER语法](#)[13.6.2.3. LOAD TABLE *tbl_name* FROM MASTER语法](#)[13.6.2.4. MASTER_POS_WAIT()语法](#)[13.6.2.5. RESET SLAVE语法](#)[13.6.2.6. SET GLOBAL SQL_SLAVE_SKIP_COUNTER语法](#)[13.6.2.7. SHOW SLAVE STATUS语法](#)[13.6.2.8. START SLAVE语法](#)[13.6.2.9. STOP SLAVE语法  ](#) 复制操作可以通过SQL界面控制。本节讨论了用于管理从属复制服务器的语句。[13.6.1节,“用于控制主服务器的SQL语句”](# "13.6.1. SQL Statements for Controlling Master Servers")讨论了用于管理主服务器的语句。 #### 13.6.2.1. CHANGE MASTER TO语法 ~~~   CHANGE MASTER TO master_def [, master_def] ... ~~~ ~~~   ~~~ ~~~ master_def: ~~~ ~~~       MASTER_HOST = 'host_name' ~~~ ~~~     | MASTER_USER = 'user_name' ~~~ ~~~     | MASTER_PASSWORD = 'password' ~~~ ~~~     | MASTER_PORT = port_num ~~~ ~~~     | MASTER_CONNECT_RETRY = count ~~~ ~~~     | MASTER_LOG_FILE = 'master_log_name' ~~~ ~~~     | MASTER_LOG_POS = master_log_pos ~~~ ~~~     | RELAY_LOG_FILE = 'relay_log_name' ~~~ ~~~     | RELAY_LOG_POS = relay_log_pos ~~~ ~~~     | MASTER_SSL = {0|1} ~~~ ~~~     | MASTER_SSL_CA = 'ca_file_name' ~~~ ~~~     | MASTER_SSL_CAPATH = 'ca_directory_name' ~~~ ~~~     | MASTER_SSL_CERT = 'cert_file_name' ~~~ ~~~     | MASTER_SSL_KEY = 'key_file_name' ~~~ ~~~     | MASTER_SSL_CIPHER = 'cipher_list' ~~~ 可以更改从属服务器用于与主服务器进行连接和通讯的参数。 MASTER_USER, MASTER_PASSWORD, MASTER_SSL, MASTER_SSL_CA, MASTER_SSL_CAPATH, MASTER_SSL_CERT, MASTER_SSL_KEY和MASTER_SSL_CIPHER用于向从属服务器提供有关如何与主服务器连接的信息。 即使对于在编译时没有SSL支持的从属服务器,SSL选项(MASTER_SSL, MASTER_SSL_CA, MASTER_SSL_CAPATH, MASTER_SSL_CERT, MASTER_SSL_KEY和MASTER_SSL_CIPHER)也可以被更改。它们被保存到master.info文件中,但是会被忽略,直到您使用一个SSL支持已启用的服务器。 如果您不指定一个给定的参数,则它会保持其原有的值。例外情况在后面的讨论中进行了说明。举例说明,如果用于连接到您的MySQL主服务器的 密码被更改了,您只需发布这些语句,就可以告知从属服务器新的密码: ~~~ mysql> STOP SLAVE; -- if replication was running ~~~ ~~~ mysql> CHANGE MASTER TO MASTER_PASSWORD='new3cret'; ~~~ ~~~ mysql> START SLAVE; -- if you want to restart replication ~~~ 没有必要指定没有改变的参数(主机、接口、用户等)。 MASTER_HOST和MASTER_PORT是主服务器主机和其TCP/IP接口的主机名(或IP地址)。注意,如果MASTER_HOST与localhost相等,那么,和MySQL的其它部分一样,接口可以被忽略(例如,如果可以使用Unix插槽文件)。 如果您指定了MASTER_HOST或MASTER_PORT,则从属服务器会假定主服务器与以前不一样(即使您指定的主机或接口值与当前值是一样的。)在此情况下,主服务器二进制日志的名称和位置的原有值不再适用,因此,如果您不指定语句中的MASTER_LOG_FILE和MASTER_LOG_POS,MASTER_LOG_FILE=''和MASTER_LOG_POS=4会被静默地添加。 在MASTER_LOG_FILE和MASTER_LOG_POS坐标点,从属服务器I/O线程在启动之后从主服务器读取。如果您只指定了其中一个,则从属服务器不能指定RELAY_LOG_FILE或RELAY_LOG_POS。如果MSATER_LOG_FILE和MASTER_LOG_POS都没有被指定,则从属服务器会使用在CHANGE MASTER被发布前的最后一个slave SQL thread坐标。当您只想改变要使用的 密码时,这可以确保复制的连续性。即使从属服务器SQL线程落后于从属服务器I/O线程,也可以确保复制的连续性。 CHANGE MASTER会删除所有的中继日志文件并启动一个新的日志,除非您指定了RELAY_LOG_FILE或RELAY_LOG_POS。在此情况下,中继日志被保持;relay_log_purge全局变量被静默地设置为0。 CHANGE MASTER TO可以更新master.info和relay-log.info文件的内容。 当您拥有主服务器快照并拥有日志和对应的偏移量时,CHANGE MASTER对于设置从属服务器是有用的。在把快照载入从属服务器之后,您可以在从属服务器上运行CHANGE MASTER TO MASTER_LOG_FILE='*log_name_on_master*', MASTER_LOG_POS=*log_offset_on_master*。 举例说明: ~~~ mysql> CHANGE MASTER TO ~~~ ~~~     ->     MASTER_HOST='master2.mycompany.com', ~~~ ~~~     ->     MASTER_USER='replication', ~~~ ~~~     ->     MASTER_PASSWORD='bigs3cret', ~~~ ~~~     ->     MASTER_PORT=3306, ~~~ ~~~     ->     MASTER_LOG_FILE='master2-bin.001', ~~~ ~~~     ->     MASTER_LOG_POS=4, ~~~ ~~~     ->     MASTER_CONNECT_RETRY=10; ~~~ ~~~   ~~~ ~~~ mysql> CHANGE MASTER TO ~~~ ~~~     ->     RELAY_LOG_FILE='slave-relay-bin.006', ~~~ ~~~     ->     RELAY_LOG_POS=4025; ~~~ 第一个例子可以更改主服务器及其二进制日志坐标。当想要设置从属服务器来复制主服务器时使用。 第二个例子显示了较少被使用的一个操作。当从属服务器含有中继日志,并且您出于某种原因想要执行此日志时使用。要这么做时,不需要连接主服务器。您只需要使用CHANGE MASTER TO并启动SQL线程(START SLAVE SQL_THREAD)。 您甚至可以在一个用于独立非从属服务器的非复制型设置中使用第二种操作,在崩溃之后进行复原。假设您的服务器已崩溃,同时您已恢复了备份。您想要重新播放服务器自己的二进制日志(不是中继日志,而是正规的二进制文件),例如名为myhost-bin.*。首先,应在安全的地方制作这些二进制日志的备份,以防您没有完全遵守以下步骤,意外地让服务器清理了二进制文件。使用SET GLOBAL relay_log_purge=0,进一步增加安全性。然后启动不含--log-bin选项的服务器。使用--replicate-same-server-id, --relay-log=myhost-bin(让服务器相信,这些正规的二进制日志是中继日志)和--skip-slave-start options选项。当服务器启动后,发布以下语句: ~~~ mysql> CHANGE MASTER TO ~~~ ~~~     ->     RELAY_LOG_FILE='myhost-bin.153', ~~~ ~~~     ->     RELAY_LOG_POS=410, ~~~ ~~~     ->     MASTER_HOST='some_dummy_string'; ~~~ ~~~ mysql> START SLAVE SQL_THREAD; ~~~ 服务器会读取并执行自己的二进制日志,完成崩溃复原。当复原完成后,运行STOP SLAVE,关闭服务器,删除master.info和relay-log.info,并使用原来的选项重新启动服务器。 要让服务器认为它是一个从属服务器,需要指定MASTER_HOST(甚至使用假值)。 #### 13.6.2.2. LOAD DATA FROM MASTER语法 ~~~ LOAD DATA FROM MASTER ~~~ 本命令用于对主服务器进行快照,并拷贝到从属服务器上。它可以更新MASTER_LOG_FILE和MASTER_LOG_POS的值,这样,从属服务器就可以从正确的位置开始进行复制。使用--replicate-*-do-*和--replicate-*-ignore-*选项指定的表和数据库排除规则均被兑现。--replicate-rewrite-db没有被考虑。这是因为使用本选项,用户就可以设置一个例如--replicate-rewrite-db=db1->db3和--replicate-rewrite-db=db2->db3的非唯一映射。当从主服务器载入表时,该映射会使从属服务器发生混淆。 本语句的使用受以下条件的制约: ·         只对MyISAM表起作用。如果试图载入一个非MyISAM表,会导致以下错误: ~~~ ·                ERROR 1189 (08S01): Net error reading from master ~~~ ·         当拍摄快照时,会获得对主服务器的全局读取锁定。在载入操作期间,该锁定会阻止对主服务器的更新。 如果您正在载入大表,您可能必须对主服务器和从属服务器均增加net_read_timeout和net_write_timeout值。请参见[5.3.3节,“服务器系统变量”](# "5.3.3. Server System Variables")。 注意,LOAD DATA FROM MASTER不从mysql数据库拷贝任何表。这可以更容易地让主服务器和从属服务器拥有不同的用户和权限。 LOAD DATA FROM MASTER语句要求用于连接主服务器的复制帐户,以便让主服务器拥有RELOAD和SUPER权限,并让所有您想要载入的主服务器表拥有SELECT权限。所有的用户不拥有SELECT权限的主服务器表均被LOAD DATA FROM MASTER忽略。这是因为主服务器会对用户隐藏它们:LOAD DATA FROM MASTER会调用SHOW DATABASES以了解要载入的主服务器数据库,但是SHOW DATABASES只会返回用户有部分权限的数据库。请参见[13.5.4.6节,“SHOW DATABASES语法”](# "13.5.4.6. SHOW DATABASES Syntax")。在从属服务器方面,发布LOAD DATA FROM MASTER的用户应拥有授权,以取消或创建被复制的数据库和表。 #### 13.6.2.3. LOAD TABLE *tbl_name* FROM MASTER语法 ~~~ LOAD TABLE tbl_name FROM MASTER ~~~ 用于把表的拷贝从主服务器转移到从属服务器。本语句的主要作用是调试LOAD DATA FROM MASTER。它要求用于连接主服务器的帐户拥有对主服务器的RELOAD和SUPER权限,并拥有对要载入的主服务器表的SELECT权限。在从属服务器方面,发布LOAD TABLE FROM MASTER的用户应拥有取消和创建表的权限。 用于LOAD DATA FROM MASTER的条件也适用于这里。举例说明,LOAD TABLE FROM MASTER仅对于MyISAM表起作用。对LOAD DATA FROM MASTER的暂停注意也适用。 #### 13.6.2.4. MASTER_POS_WAIT()语法 ~~~ SELECT MASTER_POS_WAIT('master_log_file', master_log_pos) ~~~ 这实际上是一个函数,而不是一个语句。它被用于确认,从属服务器已读取并执行了到达主服务器二进制日志的给定位置。要了解完整的描述,请参见[12.9.4节,“其他函数”](# "12.9.4. Miscellaneous Functions") #### 13.6.2.5. RESET SLAVE语法 ~~~ RESET SLAVE ~~~ 用于让从属服务器忘记其在主服务器的二进制日志中的复制位置。本语句被用于进行一个明确的启动:它会删除master.info和relay-log.info文件,以及所有的中继日志,并启动一个新的中继日志。 注释:所有的中继日志被删除,即使它们没有被从属服务器SQL线程完全的执行。(如果您已经发布了一个SLAVE语句或如果从属服务器的载入量很大,则这对于一个复制从属服务器是一个很可能出现的情况。) 存储在master.info文件中的连接信息通过使用在对应的启动选项中指定的值,被立即重新设置了。此信息包括主服务器主机、主服务器接口、主服务器用户和主服务器 密码等值。当从属服务器SQL线程被中止时,它位于正在复制的临时表的中间,并且发布了RESET SLAVE,则已被复制的临时表在从属服务器中被删除。 #### 13.6.2.6. SET GLOBAL SQL_SLAVE_SKIP_COUNTER语法 ~~~ SET GLOBAL SQL_SLAVE_SKIP_COUNTER = n ~~~ 从主服务器中跳过后面的n个事件。要复原由语句导致的复制中止,这是有用的。 仅当从属线程没有正在运行时,本语句时有效的。否则,会产生一个错误。 #### 13.6.2.7. SHOW SLAVE STATUS语法 ~~~ SHOW SLAVE STATUS ~~~ 用于提供有关从属服务器线程的关键参数的信息。如果您使用mysql客户端发布此语句,则您可以使用一个\G语句终止符来获得更便于阅读的竖向版面,而不是使用分号: ~~~ mysql> SHOW SLAVE STATUS\G ~~~ ~~~ *************************** 1. row *************************** ~~~ ~~~        Slave_IO_State: Waiting for master to send event ~~~ ~~~           Master_Host: localhost ~~~ ~~~           Master_User: root ~~~ ~~~           Master_Port: 3306 ~~~ ~~~         Connect_Retry: 3 ~~~ ~~~       Master_Log_File: gbichot-bin.005 ~~~ ~~~   Read_Master_Log_Pos: 79 ~~~ ~~~        Relay_Log_File: gbichot-relay-bin.005 ~~~ ~~~         Relay_Log_Pos: 548 ~~~ ~~~ Relay_Master_Log_File: gbichot-bin.005 ~~~ ~~~      Slave_IO_Running: Yes ~~~ ~~~     Slave_SQL_Running: Yes ~~~ ~~~       Replicate_Do_DB: ~~~ ~~~   Replicate_Ignore_DB: ~~~ ~~~            Last_Errno: 0 ~~~ ~~~            Last_Error: ~~~ ~~~          Skip_Counter: 0 ~~~ ~~~   Exec_Master_Log_Pos: 79 ~~~ ~~~       Relay_Log_Space: 552 ~~~ ~~~       Until_Condition: None ~~~ ~~~        Until_Log_File: ~~~ ~~~         Until_Log_Pos: 0 ~~~ ~~~    Master_SSL_Allowed: No ~~~ ~~~    Master_SSL_CA_File: ~~~ ~~~    Master_SSL_CA_Path: ~~~ ~~~       Master_SSL_Cert: ~~~ ~~~     Master_SSL_Cipher: ~~~ ~~~        Master_SSL_Key: ~~~ ~~~ Seconds_Behind_Master: 8 ~~~ SHOW SLAVE STATUS会返回以下字段: ·         Slave_IO_State SHOW PROCESSLIST输出的State字段的拷贝。SHOW PROCESSLIST用于从属I/O线程。如果线程正在试图连接到主服务器,正在等待来自主服务器的时间或正在连接到主服务器等,本语句会通知您。在[6.3节,“复制实施细节”](# "6.3. Replication Implementation Details")中列出了可能的状态。旧版本的MySQL在连接主服务器不成功时,允许线程继续运行。对于旧版本的MySQL,观看此字段是必须的。如果它正在运行,则无问题;如果它没有运行,则您会在Last_Error字段中发现错误(后面有说明)。 ·         Master_Host 当前的主服务器主机。 ·         Master_User 被用于连接主服务器的当前用户。 ·         Master_Port 当前的主服务器接口。 ·         Connect_Retry --master-connect-retry选项的当前值 ·         Master_Log_File I/O线程当前正在读取的主服务器二进制日志文件的名称。 ·         Read_Master_Log_Pos 在当前的主服务器二进制日志中,I/O线程已经读取的位置。 ·         Relay_Log_File SQL线程当前正在读取和执行的中继日志文件的名称。 ·         Relay_Log_Pos 在当前的中继日志中,SQL线程已读取和执行的位置。 ·         Relay_Master_Log_File 由SQL线程执行的包含多数近期事件的主服务器二进制日志文件的名称。 ·         Slave_IO_Running I/O线程是否被启动并成功地连接到主服务器上。对于旧版本的MySQL(在4.1.14和5.0.12之前),如果I/O线程已被启动,即使从属服务器仍没有连接到主服务器上,Slave_IO_Running也将被设置到YES。 ·         Slave_SQL_Running SQL线程是否被启动。 ·         Replicate_Do_DB, Replicate_Ignore_DB 使用--replicate-do-db和--replicate-ignore-db选项指定的数据库清单。 ·         Replicate_Do_Table, Replicate_Ignore_Table, Replicate_Wild_Do_Table, Replicate_Wild_Ignore_Table 使用--replicate-do-table, --replicate-ignore-table, --replicate-wild-do-table和--replicate-wild-ignore_table选项指定的表清单。 ·         Last_Errno, Last_Error 被多数最近被执行的查询返回的错误数量和错误消息。错误数量为0并且消息为空字符串意味着“没有错误”。如果Last_Error值不是空值,它也会在从属服务器的错误日志中作为消息显示。 举例说明: ~~~ Last_Errno: 1051 ~~~ ~~~ Last_Error: error 'Unknown table 'z'' on query 'drop table z' ~~~ 该消息指示,表z曾经存在于在主服务器中并已被取消了,但是它没有在从属服务器中存在过,因此对于从属服务器,DROP TABLE失败。(举例说明,在设置复制时,如果您忘记了把此表拷贝到从属服务器中,则这有可能发生。) ·         Skip_Counter 最近被使用的用于SQL_SLAVE_SKIP_COUNTER的值。 ·         Exec_Master_Log_Pos 来自主服务器的二进制日志的由SQL线程执行的上一个时间的位置(Relay_Master_Log_File)。在主服务器的二进制日志中的(Relay_Master_Log_File, Exec_Master_Log_Pos)对应于在中继日志中的(Relay_Log_File, Relay_Log_Pos)。 ·         Relay_Log_Space 所有原有的中继日志结合起来的总大小。 ·         Until_Condition, Until_Log_File, Until_Log_Pos 在START SLAVE语句的UNTIL子句中指定的值。 Until_Condition具有以下值: o        如果没有指定UNTIL子句,则没有值 o        如果从属服务器正在读取,直到达到主服务器的二进制日志的给定位置为止,则值为Master o        如果从属服务器正在读取,直到达到其中继日志的给定位置为止,则值为Relay Until_Log_File和Until_Log_Pos用于指示日志文件名和位置值。日志文件名和位置值定义了SQL线程在哪个点中止执行。 ·         Master_SSL_Allowed, Master_SSL_CA_File, Master_SSL_CA_Path, Master_SSL_Cert, Master_SSL_Cipher, Master_SSL_Key 这些字段显示了被从属服务器使用的参数。这些参数用于连接主服务器。 Master_SSL_Allowed具有以下值: o        如果允许对主服务器进行SSL连接,则值为Yes o        如果不允许对主服务器进行SSL连接,则值为No o        如果允许SSL连接,但是从属服务器没有让SSL支持被启用,则值为Ignored。 与SSL有关的字段的值对应于--master-ca, --master-capath, --master-cert, --master-cipher和--master-key选项的值。 ·         Seconds_Behind_Master 本字段是从属服务器“落后”多少的一个指示。当从属SQL线程正在运行时(处理更新),本字段为在主服务器上由此线程执行的最近的一个事件的时间标记开始,已经过的秒数。当此线程被从属服务器I/O线程赶上,并进入闲置状态,等待来自I/O线程的更多的事件时,本字段为零。总之,本字段测量从属服务器SQL线程和从属服务器I/O线程之间的时间差距,单位以秒计。 如果主服务器和从属服务器之间的网络连接较快,则从属服务器I/O线程会非常接近主服务器,所以本字段能够十分近似地指示,从属服务器SQL线程比主服务器落后多少。如果网络较慢,则这种指示不准确;从属SQL线程经常会赶上读取速度较慢地从属服务器I/O线程,因此,Seconds_Behind_Master经常显示值为0。即使I/O线程落后于主服务器时,也是如此。换句话说,本列只对速度快的网络有用。 即使主服务器和从属服务器不具有相同的时钟,时间差计算也会起作用(当从属服务器I/O线程启动时,计算时间差。并假定从此时以后,时间差保持不变)。如果从属SQL线程不运行,或者如果从属服务器I/O线程不运行或未与主服务器连接,则Seconds_Behind_Master为NULL(意义为“未知”)。举例说明,如果在重新连接之前,从属服务器I/O线程休眠了master-connect-retry秒,则显示NULL,因为从属服务器不知道主服务器正在做什么,也不能有把握地说落后多少。 本字段有一个限制。时间标记通过复制被保留,这意味着,如果一个主服务器M1本身是一个从属服务器M0,则来自M1的binlog的任何事件(通过复制来自M0的binlog的事件而产生),与原事件具有相同的时间标记。这可以使MySQL成功地复制TIMESTAMP。但是,Seconds_Behind_Master的缺点是,如果M1也收到来自客户端的直接更新,则值会随机变化,因为有时最近的M1时间来自M0,有时来自直接更新,最近的时间标记也是如此。 #### 13.6.2.8. START SLAVE语法 ~~~ START SLAVE [thread_type [, thread_type] ... ] ~~~ ~~~ START SLAVE [SQL_THREAD] UNTIL ~~~ ~~~     MASTER_LOG_FILE = 'log_name', MASTER_LOG_POS = log_pos ~~~ ~~~ START SLAVE [SQL_THREAD] UNTIL ~~~ ~~~     RELAY_LOG_FILE = 'log_name', RELAY_LOG_POS = log_pos ~~~ ~~~   ~~~ ~~~ thread_type: IO_THREAD | SQL_THREAD ~~~ 不含选项的START SLAVE会同时启动两个从属服务器线程。I/O线程从主服务器中读取查询,并把它们存储在中继日志中。SQL线程读取中继日志并执行查询。START SLAVE要求SUPER权限。 如果START SLAVE成功地启动了从属服务器线程,则会返回,不会出现错误。但是,即使在此情况下,也有可能出现这样的现象——服务器线程启动了,然后又停止了(例如,因为它们没有成功地连接到主服务器上,或者没有能读取二进制日志,或者出现了其它问题)。START SLAVE对此不会发出警告。您必须检查从属服务器的错误日志,查看是否有由从属服务器线程产生的错误消息,或者使用SHOW SLAVE STATUS检查它们是否运行正常。 您可以把IO_THREAD和SQL_THREAD选项添加到语句中,指明哪些线程将要启动。 可以添加一个UNTIL子句,指定从属服务器应启动并运行,直到SQL线程达到主服务器二进制日志中的一个给定点为止。当SQL线程达到此点时,它会停止。如果在该语句中指定了SQL_THREAD选项,则它只会启动SQL线程。否则,它会同时启动两个从属服务器线程。如果SQL线程正在运行,则UNTIL子句被忽略,并发布一个警告。 对于一个UNTIL子句,您必须同时指定一个日志文件名和位置。不要把主服务器和中继日志选项混合在一起。 UNTIL条件由一个后续的STOP SLAVE语句,或一个不包括UNTIL子句的START SLAVE语句,或一个服务器重启命令重新设置。 UNTIL子句对于调试复制操作是有用的,或者可用于促使复制操作继续,直到接近一个特定的点时为止,在此点,您想要避免让从属服务器复制一个语句。举例说明,如果在主服务上执行了一个不明智的DROP TABLE语句,您可以使用UNTIL来告知从属服务器,执行到此点就停止,不要再继续了。要查找该事件是什么,需对主服务器日志或从属中继日志使用mysqlbinlog,或通过使用SHOW BINLOG EVENTS语句。 如果您正在使用UNTIL,让从属服务器成段地处理已复制的查询,则建议您使用--skip-slave-start选项来启动从属服务器,以防止当从属服务器启动时,SQL线程运行。最好在一个选项文件中使用此选项,而不是在命令行中使用,这样,如果发生了意料外的服务器重新启动,它也不会被忘记。 SHOW SLAVE STATUS语句包括了输出字段。这些字段显示了UNTIL条件的当前值。 在以前版本的MySQL中,本语句被称为SLAVE START。在MySQL 5.1中仍然接受这种用法,以便与以前版本兼容。但现在不赞成使用。 #### 13.6.2.9. STOP SLAVE语法   ~~~ STOP SLAVE [thread_type [, thread_type] ... ] ~~~ ~~~   ~~~ ~~~ thread_type: IO_THREAD | SQL_THREAD ~~~ 用于中止从属服务器线程。STOP SLAVE要求SUPER权限。 和START SLAVE相似,本语句在使用时可以加IO_THREAD和SQL_THREAD选项,指明将被中止的线程。 在以前版本的MySQL中,本语句被称为SLAVE STOP。在MySQL 5.1中仍然接受这种用法,以便与以前版本兼容。但是现在不赞成使用。 ### 13.7. 用于预处理语句的SQL语法 MySQL 5.1对服务器一方的预制语句提供支持。如果您使用合适的客户端编程界面,则这种支持可以发挥在MySQL 4.1中实施的高效客户端/服务器二进制协议的优势。候选界面包括MySQL C API客户端库(用于C程序)、MySQL Connector/J(用于Java程序)和MySQL Connector/NET。例如,C API可以提供一套能组成预制语句API的函数调用。请参见[25.2.4节,“C API预处理语句”](# "25.2.4. C API Prepared Statements")。其它语言界面可以对使用了二进制协议(通过在C客户端库中链接)的预制语句提供支持。有一个例子是[PHP 5.0中的mysqli扩展](http://php.net/mysqli)。 对预制语句,还有一个SQL界面可以利用。与在整个预制语句API中使用二进制协议相比,本界面效率没有那么高,但是它不要求编程,因为在SQL层级,可以直接利用本界面: ·         当您无法利用编程界面时,您可以使用本界面。 ·         有些程序允许您发送SQL语句到将被执行的服务器中,比如mysql客户端程序。您可以从这些程序中使用本界面。 ·         即使客户端正在使用旧版本的客户端库,您也可以使用本界面。唯一的要求是,您能够连接到一个支持预制语句SQL语法的服务器上。 预制语句的SQL语法在以下情况下使用: ·         在编代码前,您想要测试预制语句在您的应用程序中运行得如何。或者也许一个应用程序在执行预制语句时有问题,您想要确定问题是什么。 ·         您想要创建一个测试案例,该案例描述了您使用预制语句时出现的问题,以便您编制程序错误报告。 ·         您需要使用预制语句,但是您无法使用支持预制语句的编程API。 预制语句的SQL语法基于三个SQL语句: ~~~ PREPARE stmt_name FROM preparable_stmt; ~~~ ~~~   ~~~ ~~~ EXECUTE stmt_name [USING @var_name [, @var_name] ...]; ~~~ ~~~   ~~~ ~~~ {DEALLOCATE | DROP} PREPARE stmt_name; ~~~ PREPARE语句用于预备一个语句,并赋予它名称stmt_name,借此在以后引用该语句。语句名称对案例不敏感。preparable_stmt可以是一个文字字符串,也可以是一个包含了语句文本的用户变量。该文本必须展现一个单一的SQL语句,而不是多个语句。使用本语句,‘?’字符可以被用于制作参数,以指示当您执行查询时,数据值在哪里与查询结合在一起。‘?’字符不应加引号,即使您想要把它们与字符串值结合在一起,也不要加引号。参数制作符只能被用于数据值应该出现的地方,不用于SQL关键词和标识符等。 如果带有此名称的预制语句已经存在,则在新的语言被预备以前,它会被隐含地解除分配。这意味着,如果新语句包含一个错误并且不能被预备,则会返回一个错误,并且不存在带有给定名称语句。 预制语句的范围是客户端会话。在此会话内,语句被创建。其它客户端看不到它。 在预备了一个语句后,您可使用一个EXECUTE语句(该语句引用了预制语句名称)来执行它。如果预制语句包含任何参数制造符,则您必须提供一个列举了用户变量(其中包含要与参数结合的值)的USING子句。参数值只能有用户变量提供,USING子句必须准确地指明用户变量。用户变量的数目与语句中的参数制造符的数量一样多。 您可以多次执行一个给定的预制语句,在每次执行前,把不同的变量传递给它,或把变量设置为不同的值。 要对一个预制语句解除分配,需使用DEALLOCATE PREPARE语句。尝试在解除分配后执行一个预制语句会导致错误。 如果您终止了一个客户端会话,同时没有对以前已预制的语句解除分配,则服务器会自动解除分配。 以下SQL语句可以被用在预制语句中:CREATE TABLE, DELETE, DO, INSERT, REPLACE, SELECT, SET, UPDATE和多数的SHOW语句。目前不支持其它语句。 以下例子显示了预备一个语句的两种方法。该语句用于在给定了两个边的长度时,计算三角形的斜边。 第一个例子显示如何通过使用文字字符串来创建一个预制语句,以提供语句的文本: ~~~ mysql> PREPARE stmt1 FROM 'SELECT SQRT(POW(?,2) + POW(?,2)) AS hypotenuse'; ~~~ ~~~ mysql> SET @a = 3; ~~~ ~~~ mysql> SET @b = 4; ~~~ ~~~ mysql> EXECUTE stmt1 USING @a, @b; ~~~ ~~~ +------------+ ~~~ ~~~ | hypotenuse | ~~~ ~~~ +------------+ ~~~ ~~~ |          5 | ~~~ ~~~ +------------+ ~~~ ~~~ mysql> DEALLOCATE PREPARE stmt1; ~~~ 第二个例子是相似的,不同的是提供了语句的文本,作为一个用户变量: ~~~ mysql> SET @s = 'SELECT SQRT(POW(?,2) + POW(?,2)) AS hypotenuse'; ~~~ ~~~ mysql> PREPARE stmt2 FROM @s; ~~~ ~~~ mysql> SET @a = 6; ~~~ ~~~ mysql> SET @b = 8; ~~~ ~~~ mysql> EXECUTE stmt2 USING @a, @b; ~~~ ~~~ +------------+ ~~~ ~~~ | hypotenuse | ~~~ ~~~ +------------+ ~~~ ~~~ |         10 | ~~~ ~~~ +------------+ ~~~ ~~~ mysql> DEALLOCATE PREPARE stmt2; ~~~ 预制语句的SQL语法不能被用于带嵌套的风格中。也就是说,被传递给PREPARE的语句本身不能是一个PREPARE, EXECUTE或DEALLOCATE PREPARE语句。 预制语句的SQL语法与使用预制语句API调用不同。例如,您不能使用mysql_stmt_prepare() C API函数来预备一个PREPARE, EXECUTE或DEALLOCATE PREPARE语句。 预制语句的SQL语法可以在已存储的过程中使用,但是不能在已存储的函数或触发程序中使用。 当使用预制语句时,可以在LIMIT子句中使用占位符。请参见[13.2.7节,“SELECT语法”](# "13.2.7. SELECT Syntax")。 这是MySQL参考手册的翻译版本,关于MySQL参考手册,请访问[dev.mysql.com](http://dev.mysql.com/doc/mysql/en)。 原始参考手册为英文版,与英文版参考手册相比,本翻译版可能不是最新的。