MySQL 5.1参考手册 :: 13. SQL语句语法(2)
时间:2008-05-11 来源:sdccf
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语句会从给定的数据库目录中取消这些文件和目录:
· 所有带这些扩展名的文件:
.BAK |
.DAT |
.HSH |
|
.MRG |
.MYD |
.ISD |
|
.MYI |
.db |
.frm |
|
· 名称中包含两位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服务器的客户端”。
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.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语法”。
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语法”。
如果您删除的行中包括用于AUTO_INCREMENT列的最大值,则该值被重新用于BDB表,但是不会被用于MyISAM表或InnoDB表。如果您在AUTOCOMMIT模式下使用DELETE FROM tbl_name(不含WHERE子句)删除表中的所有行,则对于所有的表类型(除InnoDB和MyISAM外),序列重新编排。对于InnoDB表,此项操作有一些例外,在15.2.6.3节,“AUTO_INCREMENT列如何在InnoDB中运行”中进行了讨论。
对于MyISAM和BDB表,您可以把AUTO_INCREMENT次级列指定到一个多列关键字中。在这种情况下,从序列的顶端被删除的值被再次使用,甚至对于MyISAM表也如此。请参见3.6.9节,“使用AUTO_INCREMENT”。
DELETE语句支持以下修饰符:
· 如果您指定LOW_PRIORITY,则DELETE的执行被延迟,直到没有其它客户端读取本表时再执行。
· 对于MyISAM表,如果您使用QUICK关键词,则在删除过程中,存储引擎不会合并索引端结点,这样可以加快部分种类的删除操作的速度。
· 在删除行的过程中,IGNORE关键词会使MySQL忽略所有的错误。(在分析阶段遇到的错误会以常规方式处理。)由于使用本选项而被忽略的错误会作为警告返回。
删除操作的速度会受到一些因素的影响,这些因素在7.2.18节,“DELETE语句的速度”中进行了讨论。
在MyISAM表中,被删除的记录被保留在一个带链接的清单中,后续的INSERT操作会重新使用旧的记录位置。要重新使用未使用的空间并减小文件的尺寸,则使用OPTIMIZE TABLE语句或myisamchk应用程序重新编排表。OPTIMIZE TABLE更简便,但是myisamchk速度更快。请参见13.5.2.5节,“OPTIMIZE TABLE语法”和第7章:优化。
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语法”中进行了说明。
对于第一个语法,只删除列于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语法”。
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语法”中对INSERT...SELECT进行了进一步的讨论。
行应被插入到tbl_name表中。可以按以下方法指定列。本语句向这些列提供值。
· 列名称清单或SET子句明确的指示了列。
· 如果您不为INSERT...VALUES或INSERT...SELECT指定列的清单,则表中每列的值必须在VALUES清单中提供,或由SELECT提供。如果您不知道表中各列的顺序,则使用DESCRIBE tbl_name查询。
列值可以采用多种方法给定:
· 如果不是在严格模式下运行,则所有没有明确给定值的列都被设置为默认值(明确的或隐含的)。例如,如果您指定了一个列清单,但此清单没有对表中所有的列进行命名,则未命名的各列被设置为默认值。默认值的赋值在13.1.5节,“CREATE TABLE语法”中进行了说明。也可参见1.8.6.2节,“对无效数据的约束”。
有时候,您需要对所有没有默认值的列明确地指定值。如果您希望,在没有明确指定值时,INSERT语句可以生成错误信息,则您应该使用STRICT模式。请参见5.3.2节,“SQL服务器模式”。
· 使用关键词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语法”。
· 如果您使用LOW_PRIORITY关键词,则INSERT的执行被延迟,直到没有其它客户端从表中读取为止。当原有客户端正在读取时,有些客户端刚开始读取。这些客户端也被包括在内。此时,INSERT LOW_PRIORITY语句等候。因此,在读取量很大的情况下,发出INSERT LOW_PRIORITY语句的客户端有可能需要等待很长一段时间(甚至是永远等待下去)。(这与INSERT DELAYED形成对比,INSERT DELAYED立刻让客户端继续执行。请参见13.2.4.2节,“INSERT DELAYED语法”。)注意LOW_PRIORITY通常不应用于MyISAM表,因为这么做会取消同时进行的插入。请参见15.1节,“MyISAM存储引擎”。
· 如果您指定了HIGH_PRIORITY,同时服务器采用--low-priority-updates选项启动,则HIGH_PRIORITY将覆盖--low-priority-updates选项。这么做还会导致同时进行的插入被取消。
· 使用mysql_affected_rows() C API函数,可以获得用于INSERT的受影响行的值。请参见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节,“信息函数”和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()”。
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.4节,“MEMORY (HEAP)存储引擎”和15.8节,“ARCHIVE存储引擎”。
· 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语句的信息:
状态变量 |
意义 |
Delayed_insert_threads |
管理程序线程的数目 |
Delayed_writes |
使用INSERT DELAYED写入的行的数目 |
Not_flushed_delayed_rows |
等待被写入的行的数目 |
· 您可以通过发送一个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语句的速度”。
由character_set_database系统变量指示的字符集被用于解释文件中的信息。SET NAMES和character_set_client的设置不会影响对输入的解释。
注意,目前不能载入UCS2数据文件。
您也可以通过使用mysqlimport应用程序载入数据文件;通过向服务器发送一个LOAD DATA INFILE语句实现此功能。--local选项用于使mysqlimport从客户主机中读取数据文件。如果客户端和服务器支持压缩协议,则您可以指定—compress选项提高在慢速网络中的性能。请参见8.10节,“mysqlimport:数据导入程序。
如果您使用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提供的权限”。
与让服务器直接读取文件相比,使用LOCAL速度略慢,这是因为文件的内容必须通过客户端发送到服务器上。不过,您不需要FILE权限来载入本地文件。
只有当您的服务器和您的客户端都许可时,LOCAL才可运行。例如,如果使用—local-infile=0启动mysqld,则LOCAL不运行。请参见5.6.4节,“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语法”。
如果您指定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语句的速度”。
LOAD DATA INFILE是SELECT...INTO OUTFILE的补语。(见13.2.7节,“SELECT语法”。)要从一个表中把数据写入一个文件中,应使用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节,“文字值”。
在特定情况下,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语法”中进行了说明。
如果字段值缺失,则对一个空字段值会被按不同方式理解:
· 对于字符串类型,列被设置为空字符串。
· 对于数字类型,列被设置为0。
· 对于日期和时间类型,列被设置为该类型相应的“zero”。请参见11.3节,“日期和时间类型”。
如果您明确地把一个空字符串赋予一个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()”。
当值通过INSERT语句被插入时或出现相同情况时,会发生警告(见13.2.4节,“INSERT语法”)。例外情况是,当输入行中字段过多或过少时,LOAD DATA INFILE也生成警告。这些警告并不存储;警告的数量只用于指示运行是否良好。
您可以使用SHOW WARNINGS来得到第一批max_error_count警告的清单,作为有关运行错误的信息。请参见13.5.4.22节,“SHOW WARNINGS语法”。
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语法”。
注意,除非表有一个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.8节,“Subquery语法”。
· 每个select_expr都指示一个您想要恢复的列。
· table_references指示行从哪个表或哪些表中被恢复。在13.2.7.1节,“JOIN语法”中对该语法进行了说明。
· 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节,“与列别名有关的问题”。
· FROM table_references子句指示行从哪些表中被恢复。如果您命名的表多于一个,则您在进行一个联合操作。要了解有关联合语法的说明,请参见13.2.7.1节,“JOIN语法”。对于每一个被指定的表,您可以自选地指定一个别名。
· tbl_name [[AS] alias]
· [{USE|IGNORE|FORCE} INDEX (key_list)]
使用USE INDEX、IGNORE INDEX、FORCE INDEX可以向优化符提示如何选择索引。这部分内容在13.2.7.1节,“JOIN语法”中进行了讨论。
您可以使用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节,“数据库、表、索引、列和别名”。
· 在没有表被引用的情况下,允许您指定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章:函数和操作符。
· 被选择的用于输出的列可以使用列名称、列别名或列位置被引用到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子句同时使用的函数和修改程序”。
· GROUP BY允许一个WITH ROLLUP修饰符。请参见12.10.2节,“GROUP BY修改程序”。
· 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语法”。
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节,“步骤分析”。
· 存储引擎使用页面或行锁。如果您对存储引擎使用FOR UPDATE,则受到查询检验的行会被进行写锁定,直到当前事务结束为止。使用LOCK IN SHARE MODE可以设置一个共享锁。共享锁可以防止其它事务更新或删除被检验的行。请参见15.2.10.5节,“锁定读取SELECT ... FOR UPDATE和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的信息)”。STRAIGHT_JOIN也可以被用于table_references清单中。请参见13.2.7.1节,“JOIN语法”。
· 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节,“信息函数”。
· 如果您正在使用一个query_cache_type值,值为2或DEMAND,则SQL_CACHE告知MySQL把查询结果存储在查询缓存中。对于使用UNION的查询或子查询,本选项会影响查询中的所有SELECT。请参见5.13节,“MySQL查询高速缓冲”。
· SQL_NO_CACHE告知MySQL不要把查询结果存储在查询缓存中。请参见5.13节,“MySQL查询高速缓冲”。对于一个使用UNION或子查询的查询,本选项会影响查询中的SELECT。