6.4 数据操纵:SELECT
, INSERT
, UPDATE
, DELETE
6.4.1 SELECT
句法
SELECT [STRAIGHT_JOIN] [SQL_SMALL_RESULT] [SQL_BIG_RESULT] [SQL_BUFFER_RESULT] [SQL_CACHE | SQL_NO_CACHE] [SQL_CALC_FOUND_ROWS] [HIGH_PRIORITY] [DISTINCT | DISTINCTROW | ALL] select_expression,... [INTO {OUTFILE | DUMPFILE} 'file_name' export_options] [FROM table_references [WHERE where_definition] [GROUP BY {unsigned_integer | col_name | formula} [ASC | DESC], ...] [HAVING where_definition] [ORDER BY {unsigned_integer | col_name | formula} [ASC | DESC] ,...] [LIMIT [offset,] rows | rows OFFSET offset] [PROCEDURE procedure_name(argument_list)] [FOR UPDATE | LOCK IN SHARE MODE]]
SELECT
用于检索从一个或多个表中选取出的行。select_expression
表示你希望检索的列。 SELECT
也可以被用于检索没有引用任何表的计算列。例如:
mysql> SELECT 1 + 1; -> 2
所有使用的关键词必须严格以上面所显示的次序被给出。举例来说,一个 HAVING
子句必须出现在 GROUP BY
子句后,在 ORDER BY
字句之前。
- 一个
SELECT
表达式可以使用AS
指定一个别名。别名可以当作表达式的列名,用于ORDER BY
或HAVING
子句中。例如:mysql> SELECT CONCAT(last_name,', ',first_name) AS full_name FROM mytable ORDER BY full_name;
- 在一个
WHERE
子句中使用一个列别名是不允许的,因为,当WHERE
子句被执行时,列值可能还没有被计算确定。查看章节 A.5.4 使用alias
的限制。 FROM table_references
子句表示从哪个表中检索记录行。如果你命名超过超过一个表,并执行一个 join。对于 join 句法的信息,查看章节 6.4.1.1JOIN
句法。对于每个引用的表,你可以顺便指定一个别名。table_name [[AS] alias] [[USE INDEX (key_list)] | [IGNORE INDEX (key_list)] | FORCE INDEX (key_list)]]
到 MySQL 3.23.12 时,当 MySQL 在从一个表中检索信息时,你可以提示它选择了哪一个索引。如果EXPLAIN
显示 MySQL 使用了可能的索引列表中错误的索引,这个特性将是很有用的。通过指定USE INDEX (key_list)
,你可以告诉 MySQL 使用可能的索引中最合适的一个索引在表中查找记录行。可选的二选一句法IGNORE INDEX (key_list)
可被用于告诉 MySQL 不使用特定的索引。 在 MySQL 4.0.9 中,你也可以使用FORCE INDEX
。这个有点像USE INDEX (key_list)
,但是有了这个附加物,一个表的扫描被采用时,将会有非常大的开销。换句法说,如果没有方法使用给定的索引在表中寻找记录行,这时表扫描才会被使用。USE/IGNORE/FORCE KEY
分别是USE/IGNORE/FORCE INDEX
的同义词。- 你可以以
tbl_name
(在当前的数据库中) 引用一张表,或以dbname.tbl_name
明确地指定其个数据。你要以以col_name
、tbl_name.col_name
或db_name.tbl_name.col_name
引用一个列。 你不需要在一个SELECT
语句中引用的列前指定tbl_name
或db_name.tbl_name
前缀,除非引用列存在二义性。查看章节 6.1.2 数据库、表、索引、列和别名,对于有歧义的列引用需要更加显式的列引用格式。 - 一个表的引用可以使用
tbl_name [AS] 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;
- 选取出来用于输出的列可以在
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;
为了以倒序排序,可以在 ORDER BY 子句中用于排序的列名后添加一个DESC
(递减 descending)关键词。缺省为升序排序;这也可以通过使用ASC
关键词明确指定。 - 在
WHERE
子句中可以使用 MySQL 支持的任何函数。查看章节 6.3 用于SELECT
和WHERE
子句的函数。 HAVING
子句可以引用任何列或在select_expression
中命名的别名。它在最后被执行,仅仅就在项目被送到客户端之前,不进行任何优化。所以不要对应该放在WHERE
子句中的项目使用HAVING
。举例来说,不要写成这样:mysql> SELECT col_name FROM tbl_name HAVING col_name > 0;
用这个代替:mysql> SELECT col_name FROM tbl_name WHERE col_name > 0;
在 MySQL 3.22.5 或以后的版本中,你也可以这下面的形式书写一个查询:mysql> SELECT user,MAX(salary) FROM users -> GROUP BY user HAVING MAX(salary)>10;
在较早的 MySQL 版本中,你可能需要用下面的代替了:mysql> SELECT user,MAX(salary) AS sum FROM users -> group by user HAVING sum>10;
DISTINCT
、DISTINCTROW
和ALL
选项指定重复的记录行是否被返回。缺省为 (ALL
),返回所有匹配的记录行。DISTINCT
和DISTINCTROW
是同义词,它指定结果集重复的记录行被排除。- 所有以
SQL_
开头、STRAIGHT_JOIN
和HIGH_PRIORITY
的选项是 MySQL 对 ANSI SQL 的扩展。 HIGH_PRIORITY
将给SELECT
语句比更新一个表有更高的优先级。你只应该对非常快的或需要立即返回的查询使用它。 如果一个表已被读锁定,甚至是有一个更新语句正在等待表的释放,一个SELECT HIGH_PRIORITY
查询也将会执行。SQL_BIG_RESULT
可以与GROUP BY
或DISTINCT
一同使用,以告诉优化器结果集将有许多记录行。在这种情况下,如果需要,MySQL 将直接使用基于磁盘的临时表。同样的,在这种情况下,MySQL 更愿意以GROUP BY
上的一个键进行排序而不是建立一个临时表。SQL_BUFFER_RESULT
将强制把结果放入一个临时表。这将有助于 MySQL 尽早地释放表和有助于将大的结果集传送到客户端。SQL_SMALL_RESULT
, 一个 MySQL 特有的选项,可以与GROUP BY
或DISTINCT
一同使用,以告诉优化器结果集将会很小。在这种情况下,MySQL 将使用快速的临时表存储结果表,而不是使用排序。在 MySQL 3.23 中,这通常是不需要的。SQL_CALC_FOUND_ROWS
(版本 4.0.0 和更新的) 告诉 MySQL 计算在不考虑LIMIT
子句时结果集中将有多少行记录。然后使用SELECT FOUND_ROWS()
可以检索到记录行的数目。查看章节 6.3.6.2 辅助功能函数。 请注意,在早于 4.1.0 的版本中,LIMIT 0
是不工作的,它将被优化为立即返回(结果集的记录数为 0)。查看章节 5.2.8 MySQL 如何优化LIMIT
。- 如果你使用了
QUERY_CACHE_TYPE=2
(DEMAND
),SQL_CACHE
告诉 MySQL 将存储查询结果放入查询高速缓存内。查看章节 6.9 MySQL 的查询高速缓存。 SQL_NO_CACHE
告诉 MySQL 不允许将查询结果存储到查询缓存内。查看章节 6.9 MySQL 的查询高速缓存。- 如果使用了
GROUP BY
,输出记录将会依照GROUP BY
列进行排序,就好像你对所有GROUP BY
中的所有字段使用了ORDER BY
。MySQL 扩展了GROUP BY
的用法,所以你也可以在GROUP BY
中指定ASC
和DESC
:SELECT a,COUNT(b) FROM test_table GROUP BY a DESC
- MySQL 扩展了的
GROUP BY
用法允许你选取没有在GROUP BY
子句中提及的字段。如果你的查询没有得到你所期望的结果,请查看GROUP BY
中的描述。查看章节 6.3.7 用于GROUP BY
子句的函数。 STRAIGHT_JOIN
强制优化器以表在FROM
子句中列出的顺序联结。如果优化器以一个非优化的次序联结各表,你可以使用它来加速一个查询。查看章节 5.2.1EXPLAIN
句法(得到有关SELECT
的信息)。LIMIT
子句可以被用于强制SELECT
语句返回指定的记录数。LIMIT
接受一个或两个数字参数。参数必须是一个整数常量。如果给定两个参数,第一个参数指定第一个返回记录行的偏移量,第二个参数指定返回记录行的最大数目。初始记录行的偏移量是 0(而不是 1): 为了与 PostgreSQL 兼容,MySQL 也支持句法:LIMIT # OFFSET #
。mysql> SELECT * FROM table LIMIT 5,10; # 检索记录行 6-15
为了检索从某一个偏移量到记录集的结束所有的记录行,可以指定第二个参数为 -1:mysql> SELECT * FROM table LIMIT 95,-1; # 检索记录行 96-last.
如果只给定一个参数,它表示返回最大的记录行数目:mysql> SELECT * FROM table LIMIT 5; # 检索前 5 个记录行
换句话说,LIMIT n
等价于LIMIT 0,n
。SELECT ... INTO OUTFILE 'file_name'
格式的SELECT
将选择的记录行写入一个文件。文件被建立在服务器主机上,并且不可以是已存在的 (不管别的,这可以防止数据库表和文件例如 `/etc/passwd' 被破坏)。你必须在服务器主机上有FILE
权限来使用这个形式的SELECT
。SELECT ... INTO OUTFILE
主要是有意于让你能够在服务主机上快速地转储一个表。如果你希望将结果文件建立在其它的主机上,而不是服务器上,你就不能使用SELECT ... INTO OUTFILE
。在这种情况下,你应该使用某些客户端程序例如mysqldump --tab
或mysql -e "SELECT ..." > outfile
产生文件来代替它。SELECT ... INTO OUTFILE
是LOAD DATA INFILE
的逆操作;语句中的export_options
部分的句法由FIELDS
和LINES
子句组成,它们与与用在LOAD DATA INFILE
语句中的相同。查看章节 6.4.9LOAD DATA INFILE
句法。 在结果文本文件中,只有下列的字符被ESCAPED BY
指定的字符转义:ESCAPED BY
字符- 在
FIELDS TERMINATED BY
中的第一个字符 - 在
LINES TERMINATED BY
中的第一个字符
ASCII 0
被转换到ESCAPED BY
后而跟一个 0 (ASCII 48
)。 上述行为的原因是,你必须 转义任何FIELDS TERMINATED BY
、ESCAPED BY
或LINES TERMINATED BY
字符,以便能可靠地将文件读回。ASCII 0
被转义是为了更容易地使用某些分页程序查看它。 因为结果文件并不需要遵从 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 服务器不能够创建一个其他用户拥有的文件,(你决不应该以 root 身份运行mysqld
),该文件必须是公共可读写的,以便于你能操作它。 - 如果你以页/行锁使用在一个存储引擎上
FOR UPDATE
,被检索的记录行将被写锁。
6.4.1.1 JOIN
句法
MySQL 支持在 SELECT
中使用下面所示的 JOIN
句法:
table_reference, table_reference table_reference [CROSS] JOIN table_reference table_reference INNER JOIN table_reference join_condition table_reference STRAIGHT_JOIN table_reference table_reference LEFT [OUTER] JOIN table_reference join_condition table_reference LEFT [OUTER] JOIN table_reference table_reference NATURAL [LEFT [OUTER]] JOIN table_reference { OJ table_reference LEFT OUTER JOIN table_reference ON conditional_expr } table_reference RIGHT [OUTER] JOIN table_reference join_condition table_reference RIGHT [OUTER] JOIN table_reference table_reference NATURAL [RIGHT [OUTER]] JOIN table_reference
table_reference
定义如下:
table_name [[AS] alias] [[USE INDEX (key_list)] | [IGNORE INDEX (key_list)] | [FORCE INDEX (key_list)]]
join_condition
定义如下:
ON conditional_expr | USING (column_list)
通常不应该在 ON
存在任何条件式,它是用于限制在结果集中有哪个行的(对于这个规则也有例外)。如果你希望哪个记录行应该在结果中,你必须在 WHERE
子句中限制它。
注意,在早于 3.23.17 的版本中,INNER JOIN
不接受一个 join_condition
!
上面所显示的最后一个 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;
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
中(即,所有在table1
存在的,但在table2
中没有对应记录的记录行)。当然,这是假定table2.id
被声明为NOT NULL
的。查看章节 5.2.6 MySQL 如何优化LEFT JOIN
和RIGHT JOIN
。 USING
(column_list)
子句指定了一个列的列表,列表的中列必须同时存在于两个表中。例如USING
子句如下所示:A LEFT JOIN B USING (C1,C2,C3,...)
它可以被定义为在语义上等同于一个这样的ON
表达式:A.C1=B.C1 AND A.C2=B.C2 AND A.C3=B.C3,...
- 两个表的
NATURAL [LEFT] JOIN
被定义为在语义上等同于使用了USING
子句指定存在于两张表中的所有列的一个INNER JOIN
或一个LEFT JOIN
。 INNER JOIN
和,
(逗号) 在语义上是等同的。都是在所有的表之间进行一个全联结。通常,在 WHERE 条件中指定表应该如何联结。RIGHT JOIN
作用类似于LEFT JOIN
。为了保持数据库边的代码上精简,LEFT JOIN
被推荐使用来代替RIGHT JOIN
。STRAIGHT_JOIN
等同于JOIN
,除了左表先于右表被读入。当联结优化器将表的顺序放错时(很少),这可用于这种情况。- 到 MySQL 3.23.12 时,当 MySQL 在从一个表中检索信息时,你可以提示它选择了哪一个索引。如果
EXPLAIN
显示 MySQL 使用了可能的索引列表中错误的索引,这个特性将是很有用的。通过指定USE INDEX (key_list)
,你可以告诉 MySQL 使用可能的索引中最合适的一个索引在表中查找记录行。可选的二选一句法IGNORE INDEX (key_list)
可被用于告诉 MySQL 不使用特定的索引。 在 MySQL 4.0.9 中,你也可以使用FORCE INDEX
。这个有点像USE INDEX (key_list)
,但是有了这个附加物,一个表的扫描被采用时,将会有非常大的开销。换句法说,如果没有方法使用给定的索引在表中寻找记录行,这时表扫描才会被使用。USE/IGNORE/FORCE KEY
分别是USE/IGNORE/FORCE INDEX
的同义词。
一些例子:
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;
查看章节 5.2.6 MySQL 如何优化 LEFT JOIN
和 RIGHT JOIN
。
6.4.1.2 UNION
句法
SELECT ... UNION [ALL] SELECT ... [UNION SELECT ...]
UNION
在 MySQL 4.0.0 中被实现。
UNION
用于将多个 SELECT
语句的结果联合到一个结果集中。
在 SELECT
中的 select_expression 部分列出的列必须具有同样的类型。第一个 SELECT
查询中使用的列名将作为结果集的列名返回。
SELECT
命令是一个普通的选择命令,但是有下列的限制:
- 只有最后一个
SELECT
命令可以有INTO OUTFILE
。
如果你不为 UNION
使用关键词 ALL
,所有返回的记录行将是唯一的,就好像你为整个返回集使用了一个 DISTINCT
。如果你指定了 ALL
,那么你将得到从所有使用的 SELECT
语句中返回的所有匹配记录行。
如果你希望对整个 UNION
结果使用一个 ORDER BY
,你应该使用圆括号:
(SELECT a FROM table_name WHERE a=10 AND B=1 ORDER BY a LIMIT 10) UNION (SELECT a FROM table_name WHERE a=11 AND B=2 ORDER BY a LIMIT 10) ORDER BY a;
6.4.2 HANDLER
句法
HANDLER tbl_name OPEN [ AS alias ] HANDLER tbl_name READ index_name { = | >= | <= | < } (value1,value2,...) [ WHERE ... ] [LIMIT ... ] HANDLER tbl_name READ index_name { FIRST | NEXT | PREV | LAST } [ WHERE ... ] [LIMIT ... ] HANDLER tbl_name READ { FIRST | NEXT } [ WHERE ... ] [LIMIT ... ] HANDLER tbl_name CLOSE
HANDLER
语句提供了直接访问 MyISAM
表存储引擎的接口。
HANDLER
语句的第一个形式打开一个表,通过后来的 HANDLER ... READ
语句使它可读取。这个表对象将不能被其它线程共享,也不会被关闭,除非线程调用 HANDLER tbl_name CLOSE
或线程关闭。
第二个形式读取指定的索引遵从那个条件并且适合 WHERE
条件的一行(或更多的,由 LIMIT
子句指定)。如果索引由几个部分组成(范围有几个列),值以逗号分隔的列表指定;如果只提供的一部分值,那么第一个列是必需的。
第三个形式从表中以索引的顺序读取匹配 WHERE
条件的一行(或更多的,由 LIMIT
子句指定)。
第四个形式(没有索引清单)从表中以自然的列顺序(在数据文件中存储的次序)读取匹配 WHERE
条件的一行(或更多的,由 LIMIT
子句指定)。如果期望做一个全表扫描,它将比 HANDLER tbl_name READ index_name
更快。
HANDLER ... CLOSE
关闭一个以 HANDLER ... OPEN
打开的表。
HANDLER
是一个稍微低级的语句。举例来说,它不提供一致性约束。更确切地说,HANDLER ... OPEN
不 接受一个表的快照,并且 不 锁定表。这就意味着在一个 HANDLER ... OPEN
被执行后,表数据仍会被 (这个或其它的线程) 修改,这些修改可能在 HANDLER ... NEXT
和 HANDLER ... PREV
扫描中才会部分地出现。
使用这个接口代替普通 SQL 的原因是:
- 它比
SELECT
快,因为:- 在
HANDLER OPEN
中,一个指定的存储引擎被分配给当前线程。 - 较少的复杂解析。
- 没有优化器和没有查询检查开销。
- 在两个处理请求之间不需要锁定使用的表。
- 接口处理机并不提供一个一致性的查看数据 (举例来说,读污染 dirty-reads 是允许的),因而,存储引擎可以做 SQL 通常不允许的优化。
- 在
- 它使得更加容易地移植一个使用对 MySQL 的 ISAM 类似接口的应用程序。
- 它允许你在一个以 SQL 不容易完成(在某些不可能的完全)的情况下遍历一个数据库。当使用提供了一个交互式的用户接口访问数据库的应用程序时,接口处理机是更加自然的查看数据的方式。
6.4.3 INSERT
句法
INSERT [LOW_PRIORITY | DELAYED] [IGNORE] [INTO] tbl_name [(col_name,...)] VALUES ((expression | DEFAULT),...),(...),... [ ON DUPLICATE KEY UPDATE col_name=expression, ... ] or INSERT [LOW_PRIORITY | DELAYED] [IGNORE] [INTO] tbl_name [(col_name,...)] SELECT ... or INSERT [LOW_PRIORITY | DELAYED] [IGNORE] [INTO] tbl_name SET col_name=(expression | DEFAULT), ... [ ON DUPLICATE KEY UPDATE col_name=expression, ... ]
INSERT
将新行插入到一个已存在的表中。INSERT ... VALUES
形式的语句基于明确的值插入记录行。INSERT ... SELECT
形式的语句从另一个或多个表中选取出值,并将其插入。有多重值列表的 INSERT ... VALUES
形式的语句在 MySQL 3.22.5 或更新的版本中被支持。col_name=expression
句法在 MySQL 3.22.10 或更新的版本中得到支持。
tbl_name
是记录将要被插入的表。列名列表或 SET
子句指出语句指定的值赋给哪个列:
- 如果在
INSERT ... VALUES
或INSERT ... SELECT
中没有指定列列表,那么所有列的值必须在VALUES()
列表中或由SELECT
提供。如果你不知道表的列的次序,可以使用DESCRIBE tbl_name
来决定它。 - 任何没有明确指定一个值的列均会被设置为它的缺省值。举例来说,如果你指定的一个列列表没有指定表中所有的列,未指定的列将被设置为它们的缺省值。缺省值赋值的描述在章节 6.5.3
CREATE TABLE
句法。 你也可以使用关键词DEFAULT
来将一个列设置为它的默认值(这在 MySQL 4.0.3 中被新加入)。这使它更加容易地书写赋予值到所有除了几列的INSERT
语句,因为它允许您避免书写一个不完全的VALUES()
的列表(在该列表没有包含表中的每个列的列值)。否则,你将不得不在VALUES()
列表中写出列列表指定对应的值。 MySQL 通常都会为每个字段设置一个缺省值。这是某些强加在 MySQL 上的,在事务型表与非事务型表中均工作。 我们的观点是在应用程序端检查字段的内容,而不是在数据库服务器端。 - 一个
expression
可以引用先前在值列表中设置的任何列。例如,你可以这样:mysql> INSERT INTO tbl_name (col1,col2) VALUES(15,col1*2);
但是不能这样:mysql> INSERT INTO tbl_name (col1,col2) VALUES(col2*2,15);
- 如果你指定关键词
LOW_PRIORITY
,INSERT
的执行将会被延迟,直到没有其它客户端正在读取表。在这种情况下,客户端不得不等待插入语句被完成,如果表被频繁地使用,那么这将会花费很长一段时间。这与INSERT DELAYED
让客户端立即继续执行正好相反。查看章节 6.4.4INSERT DELAYED
句法。注意,LOW_PRIORITY
通常不对MyISAM
使用,因为这将禁止并发的插入。查看章节 7.1MyISAM
表。 - 如果你在一个有许多条记录行值的
INSERT
中指定关键词IGNORE
,任何在表中现有的PRIMARY
或UNIQUE
键上重复的记录行均会被忽略而不被插入。如果你不指定IGNORE
,当有任何记录行在一个现有的键值上重复时,插入均会被中止。你可以通过 C API 函数mysql_info()
测定共有多少记录行被插入到表中。 - 如果你指定
ON DUPLICATE KEY UPDATE
子句(在 MySQL 4.1.0 中被新加入),并且被插入的一个记录行在PRIMARY
或UNIQUE
键上将会产生一个重复值,那么老的记录行将被UPDATE
。举例来说:mysql> INSERT INTO table (a,b,c) VALUES (1,2,3) --> ON DUPLICATE KEY UPDATE c=c+1;
假设列a
被定义为UNIQUE
,并且已存在了一个1
,它将与下面的语句产生同样的结果:mysql> UPDATE table SET c=c+1 WHERE a=1;
注意:如果列b
也是唯一的,UPDATE
命令将要被写成这样:mysql> UPDATE table SET c=c+1 WHERE a=1 OR b=2 LIMIT 1;
并且如果a=1 OR b=2
匹配几个记录行,只有 一个 记录行将被更新!大体上,在有多重UNIQUE
键的表上,你应该尽是避免使用ON DUPLICATE KEY
子句。 当使用了ON DUPLICATE KEY UPDATE
后,DELAYED
选项将被忽略。 - 如果 MySQL 被设置为使用
DONT_USE_DEFAULT_FIELDS
选项,INSERT
语句将产生一个错误,除非你为所有需要一个非NULL
值的列明确指定值。查看章节 2.3.3 典型的configure
选项。 - 通过使用
mysql_insert_id
函数你可以找到用于一个AUTO_INCREMENT
列的值。查看章节 8.1.3.130mysql_insert_id()
。
如果你使用 INSERT ... SELECT
或一个 INSERT ... VALUES
语句插入多值列,你可以使用 C API 函数 mysql_info()
得到查询的信息。信息字串的格式如下:
Records: 100 Duplicates: 0 Warnings: 0
Duplicates
指出因与某些现有的唯一索引值重复而不能被插入的记录行数目。Warnings
指出在尝试插入的列值中在某些方面可能有问题的数目。在下列任何一个条件下,警告都会发生:
- 向一个定义为
NOT NULL
的列中插入NULL
值。该列被设置为它的缺省值。 - 将一个超出列范围的值赋给一个数字列。该值被剪切到该范围内的适当的端点。
- 将一个例如
'10.34 a'
的值赋给一个数字列。尾部的无用信息将被剥离,保留数字部分并将其插入。如果该值看起来根本就不是一个数字,该列将被设置为0
。 - 将一个超出了列最大长度的字符串插入到一个
CHAR
、VARCHAR
、TEXT
或BLOB
列中。该值将被剪切到该列的最大长度。 - 将一个对列类型不合法的值插入到一个日期或时间列中。该列被适当格式的零值。
6.4.3.1 INSERT ... SELECT
句法
INSERT [LOW_PRIORITY] [IGNORE] [INTO] tbl_name [(column list)] SELECT ...
使用 INSERT ... SELECT
语句,你可以从一个或多个表中读取多个记录行,并将其快速地插入到一个表中。
INSERT INTO tblTemp2 (fldID) SELECT tblTemp1.fldOrder_ID FROM tblTemp1 WHERE tblTemp1.fldOrder_ID > 100;
一个 INSERT ... SELECT
语句有下列条件的限止:
INSERT
语句中的目标表不能在SELECT
查询部分的FROM
子句中出现,因为在 ANSI SQL 中,禁止你从正在插入的表中SELECT
。(问题是因为,SELECT
可能会发现在同一运行期内先前被插入的记录。当使用子选择子句时,这种情况将会更容易混淆!)AUTO_INCREMENT
列像平常一样工作。- 你可以使用 C API 函数
mysql_info()
得到查询的信息。查看章节 6.4.3INSERT
句法。 - 为了确保二进制日志可以被用于重建最初的表,MySQL 将不允许在
INSERT ... SELECT
期间并发的插入。
你当然也可以使用 REPLACE
代替 INSERT
来盖写老的记录行。
6.4.4 INSERT DELAYED
句法
INSERT DELAYED ...
INSERT
语句的 DELAYED
选项是一个 MySQL 特有的选项,如果你的客户端不能等待 INSERT
的完成,这将会是很有用的。This is a common problem when you use MySQL for logging and 当你打开日志记录使用 MySQL 并且你周期性的需花费很长时间才完成的 SELECT
和 UPDATE
语句时,这将是一个很普遍的问题。DELAYED
在 MySQL 3.22.15 中被引入。它是 MySQL 对 ANSI SQL92 的一个扩展。
INSERT DELAYED
仅仅工作与 ISAM
和 MyISAM
表。注意,因为 MyISAM
表支持并发的 SELECT
和 INSERT
,如果在数据文件中没有空闲的块,那你将很少需要对 MyISAM
表使用 INSERT DELAYED
。查看章节 7.1 MyISAM
表。
当你使用 INSERT DELAYED
时,客户端将立即得到一个 OK,当表不被任何其它线程使用时,该行将被插入。
使用 INSERT DELAYED
的另一个主要的好处就是,从很多客户端来的插入请求会被打包在一起并写入一个块中。这比做许多单独的插入要快的多。
注意,当前的记录行队列是被存储在内存中的,一直到他们被插入到表中。这就意味着,如果你使用强制的方法(kill -9
) 杀死 mysqld
,或者如果意外地死掉,任何没有写到磁盘中的记录行队列都将会丢失!
下面详细地描述当你为 INSERT
或 REPLACE
使用 DELAYED
选项时会发生什么。在这个描述中,“线程”是遇到一个 INSERT DELAYED
命令的线程,“处理器”是处理所有对于一个特定表的 INSERT DELAYED
语句的线程。
- 当一个线程对一个表执行一个
DELAYED
语句时,将会创建一个处理器线程用以处理对该表的所有DELAYED
语句,除非这样的处理器已经存在。 - 线程检查处理器是否已经获得了一个
DELAYED
锁;如果还没有,这告诉处理程序去获得。即使其它的线程已在表上加了一个READ
或WRITE
锁,也能获得DELAYED
锁。然而,处理器将等待所有的ALTER TABLE
锁或FLUSH TABLES
以保证表结构是最新的。 - 线程执行
INSERT
语句,但是并不将记录行写到表中,它将最终的记录行的副本放到被处理器线程管理的队列中。任何语法错误都会被线程发现并报告给客户程序。 - 客户端不能报告结果记录行中重复次数或
AUTO_INCREMENT
值;它不能从服务器获得它们,因为INSERT
早在插入操作被完成之前就返回了。如果你使用 C API,mysql_info()
函数也因同样的原因而不能获得任何有意义的信息。 - 当记录行被插入到表中时,二进制的日志文件将被处理器线程更新。对于多记录行的插入,当第一个记录行被插入时,二进制日志被更新。
- 当每写入
delayed_insert_limit
个记录行后,处理器检查是否仍有任何SELECT
语句没有解决。如果是这样,处理器允许在继续之前让这些语句先执行。 - 当处理器发现在它的队列中没有太多的记录行时,表将被解锁。如果在
delayed_insert_timeout
秒内没有接收到新的INSERT DELAYED
命令,处理器线程将终止。 - 如果在一个特定的处理器队列中已有超过
delayed_queue_size
个记录行未被解决,线程要求INSERT DELAYED
等待,只到在队列中有可用空间。这样做是为了保证mysqld
服务器对延迟内存队列不使用全部的内存。 - 处理器线程在 MySQL 进程列表中的
Command
列上显示为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
的每张表处理一个单独的线程,也是有额外的开销的。这就意味着,你应该在确定你的确需要它时才使用 INSERT DELAYED
。
6.4.5 UPDATE
句法
UPDATE [LOW_PRIORITY] [IGNORE] tbl_name SET col_name1=expr1 [, col_name2=expr2 ...] [WHERE where_definition] [ORDER BY ...] [LIMIT rows] or UPDATE [LOW_PRIORITY] [IGNORE] tbl_name [, tbl_name ...] SET col_name1=expr1 [, col_name2=expr2 ...] [WHERE where_definition]
UPDATE
以新的值更新现存表中行的列。SET
子句指出要修改哪个列和他们应该给定的值。WHERE
子句如果被给出,指定哪个记录行应该被更新。否则,所有的记录行被更新。如果 ORDER BY
子句被指定,记录行将被以指定的次序更新。
如果你指定关键词 LOW_PRIORITY
,UPDATE
的执行将被延迟,直到没有其它的客户端正在读取表。
如果你指定关键词 IGNORE
,该更新语句将不会异常中止,即使在更新过程中出现重复键错误。导致冲突的记录行将不会被更新。
如果在一个表达式中从 tbl_name
中访问一个列,UPDATE
使用列的当前值。举例来说,下面的语句设置 age
列值为它的当前值加 1 :
mysql> UPDATE persondata SET age=age+1;
UPDATE
赋值是从左到右计算的。举例来说,下列语句将 age
列设置为它的两倍,然后再加 1 :
mysql> UPDATE persondata SET age=age*2, age=age+1;
如果你设置列为其当前的值,MySQL 注意到这点,并不更新它。
UPDATE
返回实际被改变的记录行数目。在 MySQL 3.22 或更新的版本中,C API 函数 mysql_info()
返回被匹配并更新的记录行数目,以及在 UPDATE
期间发生的警告的数目。
在 MySQL 3.23 中,你可以使用 LIMIT #
来确保只有给定的记录行数目被更改。
如果一个 ORDER BY
子句被使用(从 MySQL 4.0.0 开始支持),记录行将以指定的次序被更新。这实际上只有连同 LIMIT
一起才有用。
从 MySQL 4.0.4 开始,你也可以执行一个包含多个表的 UPDATE
的操作:
UPDATE items,month SET items.price=month.price WHERE items.id=month.id;
注意:多表 UPDATE
不可以使用 ORDER BY
或 LIMIT
。
6.4.6 DELETE
句法
DELETE [LOW_PRIORITY] [QUICK] FROM table_name [WHERE where_definition] [ORDER BY ...] [LIMIT rows] or DELETE [LOW_PRIORITY] [QUICK] table_name[.*] [, table_name[.*] ...] FROM table-references [WHERE where_definition] or DELETE [LOW_PRIORITY] [QUICK] FROM table_name[.*] [, table_name[.*] ...] USING table-references [WHERE where_definition]
DELETE
从 table_name
中删除 where_definition
中给定条件的记录行,并返回删除的记录数目。
如果你发出一个没有 WHERE
子句的 DELETE
,所有的记录行将被删除。如果你以 AUTOCOMMIT
模式执行它,那么它类似于 TRUNCATE
。查看章节 6.4.7 TRUNCATE
句法。在 MySQL 3.23 中,没有一个 WHERE
子句的 DELETE
将返回零作为受影响的记录数目。
当你删除所有记录行时,如果你真的希望知道多少条记录被删除,你可以使用一个这种形式的 DELETE
语句:
mysql> DELETE FROM table_name WHERE 1>0;
注意,这将比一个没有 WHERE
子句的 DELETE FROM table_name
语句慢,因为它一次只删除一行。
如果你指定关键词 LOW_PRIORITY
,DELETE
的执行将被延迟,直到没有其它的客户端正在读取表。
如果你指定关键词 QUICK
,那么在删除过程中存储引擎将不会归并索引叶,这可能会加速某些类型的删除操作。
在 MyISAM
表中,删除了的记录被放在一个链接表中维护,以后的 INSERT
操作将重新使用删除后的记录位置。为了回收闲置的空间,并减小文件尺寸,使用 OPTIMIZE TABLE
语句或 myisamchk
实用程序重新组织表。OPTIMIZE TABLE
使用比较容易,但是 myisamchk
更快点。查看章节 4.5.1 OPTIMIZE TABLE
句法 和章节 4.4.6.10 表优化。
第一个多表删除格式从 MySQL 4.0.0 开始被支持。第二个多表删除格式从 MySQL 4.0.2 开始被支持。
仅仅在 FROM
或 USING
子句 之前 列出的表中的匹配记录行被删除。效果就是,你要以从多个表中同时删除记录行,并且同样可以有其它的表用于检索。
在表名后的 .*
仅仅是为了兼容 Access
:
DELETE t1,t2 FROM t1,t2,t3 WHERE t1.id=t2.id AND t2.id=t3.id or DELETE FROM t1,t2 USING t1,t2,t3 WHERE t1.id=t2.id AND t2.id=t3.id
在上面的情况下,我们仅仅从 t1
和 t2
表中删除匹配的记录行。
如果一个 ORDER BY
子句被使用(从 MySQL 4.0.0 开始支持), 记录行将以指定的次序删除。这实际上只有连同 LIMIT
一起才有用。示例如下:
DELETE FROM somelog WHERE user = 'jcole' ORDER BY timestamp LIMIT 1
这将删除匹配 WHERE
子句的,并且最早被插入(通过 timestamp
来确定)的记录行。
DELETE
语句的LIMIT rows
选项是 MySQL 特有的,它告诉服务器在控制权被返回到客户端之前可被删除的最大记录行数目。这可以用来确保一个特定的 DELETE
命令不会占用太长的时间。你可以简单地重复使用 DELETE
命令,直到被影响的记录行数目小于 LIMIT
值。
从 MySQL 4.0 开始,在 DELETE
语句中可以指定多个表,用以从一个表中删除依赖于多表中的特殊情况的记录行。然而,在一个多表删除中,不能使用 ORDER BY
或 LIMIT
。
6.4.7 TRUNCATE
句法
TRUNCATE TABLE table_name
在 3.23 中,TRUNCATE TABLE
被映射为 COMMIT ; DELETE FROM table_name
。查看章节 6.4.6 DELETE
句法。
在下面的方式中,TRUNCATE TABLE
不同于 DELETE FROM ...
:
- 删简操作撤销并重建表,这将比一个接一个地删除记录行要快得多。
- 非事务安全的;如果存在一个活动的事务或一个有效的表锁定,你将会得到一个错误。
- 不返回删除了的记录行数目。
- 只要表定义文件 `table_name.frm' 是有效的,即使数据或索引文件已经被损坏,也可以通过这种方式重建表。
TRUNCATE
是一个 Oracle SQL 的扩展。
6.4.8 REPLACE
句法
REPLACE [LOW_PRIORITY | DELAYED] [INTO] tbl_name [(col_name,...)] VALUES (expression,...),(...),... or REPLACE [LOW_PRIORITY | DELAYED] [INTO] tbl_name [(col_name,...)] SELECT ... or REPLACE [LOW_PRIORITY | DELAYED] [INTO] tbl_name SET col_name=expression, col_name=expression,...
REPLACE
功能与 INSERT
完全一样,除了如果在表中存在一个老的记录与新记录在一个 UNIQUE
或 PRIMARY KEY
上有相同的值,那么在新记录被插入之前,老的记录将被删除。查看章节 6.4.3 INSERT
句法。
换句话说,你不可以从一个 REPLACE
中访问老的记录行的值。某些老的 MySQL 版本中,你或许可以这样做,但是这是一个 Bug,现在已被修正了。
为了能够使用 REPLACE
,你必须有对该表的 INSERT
和 DELETE
权限。
当你使用一个 REPLACE
时,如果新的记录行代替了老的记录行,mysql_affected_rows()
将返回 2。这是因为在新行被插入之前,重复记录行被先删除了。
这个事实使得判断 REPLACE
是否是添加一条记录还是替换一条记录很容易:检查受影响记录行的值是 1 (添加)还是 2(替换)。
注意,除非你使用一个 UNIQUE
索引或 PRIMARY KEY
,使用 REPLACE
命令是没有感觉的,因为它会仅仅执行一个 INSERT
。
6.4.9 LOAD DATA INFILE
句法
LOAD DATA [LOW_PRIORITY | CONCURRENT] [LOCAL] INFILE 'file_name.txt' [REPLACE | IGNORE] INTO TABLE tbl_name [FIELDS [TERMINATED BY '/t'] [[OPTIONALLY] ENCLOSED BY ''] [ESCAPED BY '//' ] ] [LINES TERMINATED BY '/n'] [IGNORE number LINES] [(col_name,...)]
LOAD DATA INFILE
语句以非常高的速度从一个文本文件中读取记录行并插入到一个表中。如果 LOCAL
关键词被指定,文件从客户端主机读取。如果 LOCAL
没有被指定,文件必须位于服务器上。(LOCAL
在 MySQL 3.22.6 或更新的版本中被支持。)
由于安全性的原因,当读取位于服务器端的文本文件时,文件必须处于数据库目录或可被所有人读取的地方。同时,为了对服务器端的文件使用 LOAD DATA INFILE
,你必须在服务器主机上有 FILE
权限。查看章节 4.2.7 由 MySQL 提供的权限。
在 MySQL 3.23.49 和 MySQL 4.0.2 中,只有当你没有以 --local-infile=0
选项启动 mysqld
,或你没有禁止你的客户端程序支持 LOCAL
的情况下,LOCAL
才会工作。查看章节 4.2.4 LOAD DATA LOCAL 的安全性问题.
如果你指定关键词 LOW_PRIORITY
,LOAD DATA
语句的执行将会被延迟,直到没有其它的客户端正在读取表。
如果你对一个 MyISAM
表指定关键词 CONCURRENT
,那么当 LOAD DATA
正在执行时,其它的线程仍可以从表中检索数据。使用这个选项时,如果同时也有其它的线程正在使用表,这当然会有一点影响 LOAD DATA
的执行性能。
使用 LOCAL
将比让服务器直接访问文件要慢一些,因为文件的内容必须从客户端主机传送到服务器主机。而在另一方面,你不再需要有 FILE
权限用于装载本地文件。
如果你使用先于 MySQL 3.23.24 的版本,你不能够以 LOAD DATA INFILE
读取一个 FIFO 。如果你需要从一个 FIFO (例如,gunzip 的输出文件) 中读取,可以使用 LOAD DATA LOCAL INFILE
代替。
你也可以使用 mysqlimport
实用程序装载数据文件;它通过发送一个 LOAD DATA INFILE
命令到服务器来动作。--local
选项使得 mysqlimport
从客户端主机读取数据文件。如果客户端与服务器支持压缩协议,你可以指定 --compress
选项,以在较慢的网络中获得更好的性能。
当从服务器主机定位文件时,服务器使用下列规则:
- 如果给定一个完整的路径,服务器使用该路径名。
- 如果给定一个有一个或多个前置构件的相对路径,服务器以相对服务器的数据目录搜索文件。
- 如果给定一个没有前置构件的文件名,服务器从当前数据库的数据库目录搜寻文件。
注意,这些规则意味着,一个以 `./myfile.txt' 给出的文件是从服务器的数据目录中读取的,然而,以 `myfile.txt' 给出的一个文件是从当前数据库的数据目录下读取的。举例来说,下面的 LOAD DATA
语句从 db1
数据库目录下读取文件 `data.txt',因为 db1
是当前数据库,即使该语句明确地指定读取的文件被放入到 db2
数据库中的一个表中:
mysql> USE db1; mysql> LOAD DATA INFILE "data.txt" INTO TABLE db2.my_table;
REPLACE
和 IGNORE
关键词控制对与现有的记录在唯一键值上重复的记录的处理。如果你指定 REPLACE
,新的记录行将替换有相同唯一键值的现有记录行。如果你指定 IGNORE
,将跳过与现有的记录行在唯一键值上重复的输入记录行。如果你没有指定任何一个选项,当重复键值出现时,将会发生一个错误,文本文件的剩余部分也将被忽略。
如果你使用 LOCAL
关键词从一个本地文件中读取数据,在此操作过程中,服务器没有办法停止文件的传送,因此缺省的处理方式就好像是 IGNORE
被指定一样。
如果你在一个空的 MyISAM
表上使用 LOAD DATA INFILE
,所有非唯一索引会以一个分批方式被创建(就像 REPAIR
)。当有许多索引时,这通常可以使 LOAD DATA INFILE
更快一些。
LOAD DATA INFILE
的 SELECT ... INTO OUTFILE
的逆操作。查看章节 6.4.1 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'
换句话说,当读取输入时,缺省值导致 LOAD DATA INFILE
表现如下:
- 在换行符处寻找行的边界。
- 在定位符处将行分开放到字段中。
- 不认为字段由任何引号字符封装。
- 将有 “/” 开头的定位符、换行符或 `/' 解释为字段值的一个文字字符。
相反的,当写入输出时,缺省值导致 SELECT ... INTO OUTFILE
表现如下:
- 在字段值间加上定位符。
- 不用任何引号字符封装字段。
- 使用 “/” 转义出现在字段值中的定位符、换行符或 `/' 字符实例。
- 在行的结尾处加上换行符。
注意,为了写 FIELDS ESCAPED BY '//'
,你必须指定两个反斜线,该值会作为一个反斜线被读入。
IGNORE number LINES
选项可被用于忽略文件开头处的一个列名的头:
mysql> LOAD DATA INFILE "/tmp/file_name" INTO TABLE test IGNORE 1 LINES;
当你一前一后地使用 SELECT ... INTO OUTFILE
和 LOAD DATA INFILE
将数据从一个数据库写到一个文件中,然后再从文件中将它读入数据库中时,两个命令的字段和行处理选项必须匹配。否则,LOAD DATA INFILE
将不能正确地解释文件内容。假设你使用 SELECT ... INTO OUTFILE
以逗号分隔字段的方式将数据写入到一个文件中:
mysql> SELECT * INTO OUTFILE 'data.txt' -> FIELDS TERMINATED BY ',' -> FROM ...;
为了将由逗号分隔的文件读回时,正确的语句应该是:
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 格式的文件,字段以逗号分隔并以双引号包围着。如果文件中的行以一个换行符终止,那么下面所示的可以说明你将用来装载文件的字段和行处理选项:
mysql> LOAD DATA INFILE 'data.txt' INTO TABLE tbl_name -> FIELDS TERMINATED BY ',' ENCLOSED BY '"' -> LINES TERMINATED BY '/n';
任何字段和行处理选项都可以指定一个空字符串(''
)。如果不是空的,FIELDS [OPTIONALLY] ENCLOSED BY
和 FIELDS ESCAPED BY
值必须是一个单个字符。FIELDS TERMINATED BY
和 LINES TERMINATED BY
值可以超过一个字符。例如,为了写入由回车换行符终止的行,或读取包含这样的行的文件,应该指定一个 LINES TERMINATED BY '/r/n'
子句。
举例来说,为了读取一个文件到一个 SQL 表中,文件以一行 %%
分隔(开玩笑的),你可以这样做:
CREATE TABLE jokes (a INT NOT NULL AUTO_INCREMENT PRIMARY KEY, joke TEXT NOT NULL); LOAD DATA INFILE "/tmp/jokes.txt" INTO TABLE jokes FIELDS TERMINATED BY "" LINES TERMINATED BY "/n%%/n" (joke);
FIELDS [OPTIONALLY] ENCLOSED BY
控制字段的包围字符。对于输出 (SELECT ... INTO OUTFILE
),如果你省略单词 OPTIONALLY
,所有的字段被 ENCLOSED BY
字符包围。这样的一个输出文件(以一个逗号作为字段分界符)示例如下:
"1","a string","100.20" "2","a string containing a , comma","102.20" "3","a string containing a /" quote","102.20" "4","a string containing a /", quote and comma","102.20"
如果你指定 OPTIONALLY
,ENCLOSED BY
字符仅被作用于包围 CHAR
和 VARCHAR
字段:
1,"a string",100.20 2,"a string containing a , comma",102.20 3,"a string containing a /" quote",102.20 4,"a string containing a /", quote and comma",102.20
注意,在一个字段值中出现的 ENCLOSED BY
字符,通过用 ESCAPED BY
字符作为其前缀对其转义。同时也要注意,如果你指定一个空的 ESCAPED BY
值,可能会产生不能被 LOAD DATA 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
不会影响它。) 由ESCAPED BY
字符领先于 ENCLOSED BY
字符的出现,将被解释为当前字段值的一部分。另外,在字段中出现的重复的 ENCLOSED 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'
,而不是一个零值字节)
如果 FIELDS ESCAPED BY
字符为空,没有字符被转义。指定一个空的转义字符可能不是一个好的主意,特别是如果你的数据字段值中包含刚才列表中的任何字符时。
对于输入,如果 FIELDS ESCAPED BY
字符不为空,该字符的出现将会被剥离,后续的字符在字面上做为字段值的一部分。除了一个转义的 “0” 或 “N” (即,/0
或/N
,如果转义字符为 `/')。这些序列被解释为 ASCII 0
(一个零值字节) 和 NULL
。查看下面的有关 NULL
处理的规则。
关于更多的 “/” 转义句法信息,查看章节 6.1.1 文字:怎么写字符串与数字。
在某些情况下,字段与行处理相互作用:
- 如果
LINES TERMINATED BY
是一个空字符串,FIELDS TERMINATED BY
是非空的,行也用FIELDS TERMINATED BY
终止。 - 如果
FIELDS TERMINATED BY
和FIELDS ENCLOSED BY
值都是空的 (''
),一个固定行(无定界符) 格式被使用。用固定行格式时,在字段之间不使用分隔符。代替的,列值的写入和读取使用列的“显示”宽度。例如,如果一个列被定义为INT(7)
,列的值将使用 7 个字符的字段被写入。对于输入,列值通过读取 7 个字符来获得。固定行格式也影响对NULL
值的处理;见下面。注意,如果你正在使用一个多字节的字符集,固定长度格式将不能工作。
NULL
值的处理有很多,取决于你所使用的 FIELDS
和 LINES
选项:
- 对于缺省的
FIELDS
和LINES
值,输出时,NULL
被写成/N
,当读入时,/N
被作为NULL
读入(假设ESCAPED BY
字符为 “/”)。 - 如果
FIELDS ENCLOSED BY
是非空的,一个字段包含文字词NULL
的,它的值做为一个NULL
值被读入 (这不同于被FIELDS ENCLOSED BY
包围的词NULL
,它是被作为'NULL'
读入的)。 - 如果
FIELDS ESCAPED BY
是空的,NULL
值被写为词NULL
。 - 用固定行格式时 (它发生于
FIELDS TERMINATED BY
和FIELDS ENCLOSED BY
两者均为空),NULL
被写为一个空的字符串。注意,当将表中的NULL
值和空字符串一起写到文件中时,它们将被混淆,因为它们都是作为空字符串被写入的。如果你在文件时,需要对他们两个进行区分,你不应该使用固定行格式。
一些不能被 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
认为输入行包含表列中所有的字段。使用缺省的 FIELDS
和 LINES
值。
如果你希望装载表中的某些列,那指定一个字段列表:
mysql> LOAD DATA INFILE 'persondata.txt' -> INTO TABLE persondata (col1,col2,...);
如果输入文件的字段次序不同于表中列的顺序,你也必须指定一个字段列表。否则 MySQL 不知道如何将输入字段与表中的列匹配。
如果一个行有很少的字段,没有输入字段的列将被设置为缺省值。缺省值赋值在章节 6.5.3 CREATE TABLE
句法 中被描述。
一个空的字段值不同于字段值丢失的解释:
- 对于字符串类型,列被设置为空字符串。
- 对于数字类型,列被设置为
0
。 - 对于日期和时间类型,列被设置为适合列类型的“零”值。查看章节 6.2.2 Date 和 Time 类型。
注意,如果在一个 INSERT
或 UPDATE
语句中明确地将一个空字符串赋给一个字符串、数字或日期或时间类型,你会得到与上面相同的结果。
如果对 TIMESTAMP
列指定一个 NULL
值,或者当字段列表被指定时, TIMESTAMP
在字段列表中被遗漏(仅仅第一个 TIMESTAMP
列被影响),TIMESTAMP
列会被设置为当前的日期和时间。
如果输入的记录行有太多的字段,多余的字段将被忽略,并增加警告的数目。
LOAD DATA INFILE
认为所有的输入均是字符串,因而,对于 ENUM
或 SET
列,你不能以 INSERT
语句的形式为其设置数字值。所有的 ENUM
和 SET
必须以字符串指定!
如果你正在使用 C API,当 LOAD DATA INFILE
查询结束时,你可以调用 API 函数 mysql_info()
获得有关查询的信息。信息串的格式如下:
Records: 1 Deleted: 0 Skipped: 0 Warnings: 0
警告会在某些情况下发生,这些情况与值通过 INSERT
语句插入时发生警告的情况一样 (查看章节 6.4.3 INSERT
句法),但是 LOAD DATA INFILE
有一点与它不一样,当在输入行中有太多或过少的字段,它也会产生警告。警告不会被存储在任何地主;警告的数目仅能表示一切是否顺利。如果得到警告,并希望确切地知道为什么会得到它们,一个方法就是使用 SELECT ... INTO OUTFILE
,将它保存到另外一个文件中,并与原先的输入文件进行比较。
如果你需要 LOAD DATA
从一个管道中读取,你可以使用下面的技巧:
mkfifo /mysql/db/x/x chmod 666 /mysql/db/x/x cat < /dev/tcp/10.1.1.12/4711 > /nt/mysql/db/x/x mysql -e "LOAD DATA INFILE 'x' INTO TABLE x" x
如果你使用的版本早于 MySQL 3.23.25,你只能通过 LOAD DATA LOCAL INFILE
来执行上面。
有关 INSERT
相对 LOAD DATA INFILE
的效率和加快 LOAD DATA INFILE
的更多信息,请查看章节 5.2.9 INSERT
查询的速度。
6.4.10 DO
句法
DO expression, [expression, ...]
执行表达式,但不返回任何结果。这是 SELECT expression, expression
的一个缩写,但是当你并不关心结果时,它稍有点优势,因为它稍稍快一点。
这主要有益于有副作用的函数,比如 RELEASE_LOCK
。