php /* InnoDB事务模型和锁定 15.2.10.1. InnoDB锁定模式 15.2.10.2. InnoDB和AUTOCOMMIT 15.2.10.3. InnoDB和TRANSACTION ISOLATION LEVEL 15.2.10.4. 持续非锁定读 15.2.10.5. 锁定读SELECT ... FOR UPDATE和SELECT ... LOCK IN SHARE MODE 15.2.10.6. Next-Key锁定:避免匪夷所思的问题 15.2.10.7. 持续读如何在InnoDB中作用的例子 15.2.10.8. 在InnoDB中用不同的SQL语句设定锁 15.2.10.9. MySQL什么时候提交或回滚一个事务? 15.2.10.10. 死锁检测&回滚 15.2.10.11. 如何应对死锁 在InnoDB事务模式中,目的是把多版本数据库的最好特性与传统的二相锁定合并起来。InnoDB以Oracle的风 格,对行级进行锁定,并且默认运行查询作为非锁定持续读。在InnoDB中锁定的表被存储得如此节省空间, 以至于不需要锁定增大:典型地,数个用户被允许在数据库中锁定每一行,或者行的任何随机子集, 而InnoDB不会耗尽内存。 15.2.10.1. InnoDB锁定模式 InnoDB实现标准行级锁定,在这里有两种类型的锁: locks: · 共享的(S)锁允许一个事务去读一行(tuple)。 · 独占的锁(X)允许一个事务更新或删除一行。 如果事务A 在tuple t上持有独占锁定,来自不同事务B的对t上任一类型的锁的请求不被马上许可,取而代之 地,事务B 不得不等待事务t释放在tuple t上的锁。 如果事务A 在tuple t上持有一个共享的锁(S),那么 · 来自不同的事务B对在t 上X的锁定请求不能被马上许可。 · 来自不同的事务B对在t 上S的锁定请求可以被马上获准。因此A和B持有t上的S锁定。 不仅如此,InnoDB支持多间隔尺寸锁定,它允许记录锁和对整个表的锁共存。要使得多间隔尺寸级别的锁定实 际化,额外类型的锁,被称为intention locks被使用。在InnoDB中,意图锁定是表锁定。对于一个事务,意图 锁定之后理想的是指明在该表中对一个行随后需要哪一类型的锁定(共享还是独占)。有两种意图锁被用 在InnoDB中(假设事务T 在表R中要求一个已指出的类型的锁): · 意图共享(IS):事务T 意图给表T上单独的tuple设置S 锁定。 · 意图独占(IX):事务T 意图给这些tuple设置X 锁定。 意图锁协议如下: · 在假设的事务可以获得对某假定行的S 锁定之前,它必须首先获得对包含该行的表的一个IS 或者更强的 锁定。 · 在假设的事务可以获得对某假定行的X 锁定之前,它必须首先获得对包含该行的表的一个IX 锁定。 这些结果可以方便地用一个锁类型兼容矩阵来总结: X IX S IS X 冲突冲突冲突冲突 IX 冲突兼容冲突兼容 S 冲突冲突兼容兼容 IS 冲突兼容兼容兼容 如果一个锁定与现在锁定兼容的话,它被授给一个委托事务。如果一个锁定与现存锁定冲突,它就不被授予一 个委托事务。事务等待着直到冲突的现存锁定被释放掉。如果一个锁定请求与现存锁定相冲突,且不能被授 予,因为它可能会导致死锁,一个错误产生。 因此,意图锁定不阻碍任何东西,除了完全表请求(比如LOCK TABLES ... WRITE)。IX 和IS锁定的主要目的 是显示某人正锁定一行,或将要在表中锁定一行。 下列的例子演示当锁定请求可能会导致死锁之时一个错误会如何发生。例子中包括两个客户端A和B。 首先客户端A创建一个包含一个行的表,然后开始一个事务。在这个事务内,A通过在共享模式选择行获得对行 的S 锁定: mysql> CREATE TABLE t (i INT) ENGINE = InnoDB; Query OK, 0 rows affected (1.07 sec) mysql> INSERT INTO t (i) VALUES(1); Query OK, 1 row affected (0.09 sec) mysql> START TRANSACTION; Query OK, 0 rows affected (0.00 sec) mysql> SELECT * FROM t WHERE i = 1 LOCK IN SHARE MODE; +------+ | i | +------+ | 1 | +------+ 1 row in set (0.10 sec) 接着,客户端B开始一个事务并尝试从该表删除行: mysql> START TRANSACTION; Query OK, 0 rows affected (0.00 sec) mysql> DELETE FROM t WHERE i = 1; 删除操作要求一个X 锁定。因为这个锁定不兼容客户端A持有的S锁定,所以X 锁定不被 允许,所以请求进入 对行及客户端阻挡的锁定请求队列。 最后,客户端A也试图从表中删除该行: mysql> DELETE FROM t WHERE i = 1; ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction 因为客户端A需要一个X 锁定来删除该行,所以在这里发生死锁。尽管如此,锁定请求不被允许,因为客户 端B已经有一个对X锁定的请求并且它正等待客户端A释放S锁定。因为客户端B之前对X 锁定的请求,被客户 端A持有的S锁定也不能升级到X锁定。因此,InnoDB对客户端A产生一个错误,并且释放它的锁定。在那一点 上,客户端B的锁定请求可以被许可,并且客户端B从表中删除行。 15.2.10.2. InnoDB和 AUTOCOMMIT 在InnoDB中,所有用户行为都在事务内发生。如果自动提交模式被允许,每个SQL语句在它自己上形成一个单 独的事务。MySQL总是带着允许自动提交来开始一个新连接。 如果自动提交模式被用SET AUTOCOMMIT = 0关闭,那么我们可以认为一个用户总是有一个事务打开着。一 个SQL COMMIT或ROLLBACK语句结束当前事务并且一个新事务开始。两个语句都释放所有在当前事务中被设 置的InnoDB锁定。一个COMMIT语句意味着在当前事务中做的改变被生成为永久的,并且变成其它用户可见 的。一个ROLLBACK语句,在另一方面,撤销所有当前事务做的修改。 如果连接有被允许的自动提交,通过用明确的START TRANSACTION或BEGIN语句来开始一个事务,并 用COMMIT或者ROLLBACK语句来结束它,这样用户仍旧可以执行一个多重语句事务。 15.2.10.3. InnoDB和TRANSACTION ISOLATION LEVEL 按照SQL:1992 事务隔离级别,InnoDB默认是可重复读的(REPEATABLE READ)。MySQL/InnoDB 提 供SQL标准所描述的所有四个事务隔离级别。你可以在命令行用--transaction-isolation选项,或在选项文件 里,为所有连接设置默认隔离级别。例如,你可以在my.inf文件的[mysqld]节里类似如下设置该选 项:globally [mysqld] transaction-isolation = {READ-UNCOMMITTED | READ-COMMITTED | REPEATABLE-READ | SERIALIZABLE} 用户可以用SET TRANSACTION语句改变单个会话或者所有新进连接的隔离级别。它的语法如下: SET [SESSION | GLOBAL] TRANSACTION ISOLATION LEVEL {READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SERIALIZABLE} 注意,对--transaction-isolation选项的级别名中有连字符,但在对SET TRANSACTION语句的级别名中没有。 默认的行为是为下一个(未开始)事务设置隔离级别。如果你使用GLOBAL关键字,语句在全局对从那点开始 创建的所有新连接(除了不存在的连接)设置默认事务级别。你需要SUPER全县来做这个。使用SESSION 关 键字集为将来在当前连接上执行的事务设置默认事务级别。 任何客户端都能自由改变会话隔离级别(甚至在事务的中间),或者为下一个事务设置隔离级别。 你可以用下列语句查询全局和会话事务隔离级别: SELECT @@global.tx_isolation; SELECT @@tx_isolation; 在行级锁定中,InnoDB使用next-key锁定。这意味着除了索引记录,InnoDB也可以把索引记录前的间隙锁定 到其它用户所做的紧接该索引记录之前的块插入上。一个next-key锁定指向一个锁定住一个索引记录和它之前 的间隙的锁定。一个间隙锁定指仅锁住一些索引记录之前的间隙的锁定。 InnoDB中每个隔离级别的详细描述如下: · READ UNCOMMITTED SELECT语句以非锁定方式被执行,但是一个可能更早期版本的记录会被用到。因此,使用这个隔离级别,比 如,读是不连贯的。着也被称为“脏读”(dirty read)。另外,这个隔离级别象READ COMMITTED一样作用。 · READ COMMITTED 一个有些象Oracle的隔离级别。所有SELECT ... FOR UPDATE和SELECT ... LOCK IN SHARE MOD语句仅锁定索 引记录,而不锁定记录前的间隙,因而允许随意紧挨着已锁定的记录插入新记录。UPDATE和DELETE语句使用 一个带唯一搜索条件的唯一的索引仅锁定找到的索引记录,而不包括记录前的间隙。在范围类 型UPDATE和DELETE语句,InnoDB必须对范围覆盖的间隙设置next-key锁定或间隙锁定以及其它用户做的块 插入。这是很必要的,因为要让MySQL复制和恢复起作用,“幽灵行”必须被阻止掉。 持续读行为如同在Oracle中:即使在同一事务内, 每个持续读设置并读取它自己的新快照。请参 阅15.2.10.4节,“持续非锁定读”。 · REPEATABLE READ 这是InnoDB的默认隔离级别。带唯一搜索条件使用唯一索引的SELECT ... FOR UPDATE, SELECT ... LOCK IN SHARE MODE, UPDATE 和DELETE语句只锁定找到的索引记录,而不锁定记录前的间隙。用其它搜索条件,这 些操作采用next-key锁定,用next-key锁定或者间隙锁定锁住搜索的索引范围,并且阻止其它用户的新插入。 在持续读中,有一个与之前隔离级别重要的差别:在这个级别,在同一事务内所有持续读读取由第一次读所确 定的同一快照。这个惯例意味着如果你在同一事务内发出数个无格式SELECT语句,这些SELECT语句对相互之 间也是持续的,请参阅15.2.10.4节,“持续非锁定读”。 · SERIALIZABLE 这个级别类似REPEATABLE READ,但是所有无格式SELECT语句被隐式转换成SELECT ... LOCK IN SHARE MODE。 15.2.10.4. 持续非锁定读 持续读意味着InnoDB使用它的多版本化来给一个查询展示某个时间点处数据库的快照。查询看到在那个时间点 之前被提交的那些确切事务做的更改,并且没有其后的事务或未提交事务做的改变。这个规则的例外是,查询 看到发布该查询的事务本身所做的改变。 如果你运行在默认的REPEATABLE READ隔离级别,则在同一事务内的所有持续读读取由该事务中第一个这样 的读所确立的快照。你可以通过提交当前事务并在发布新查询的事务之后,为你的查询获得一个更新鲜的快 照。 持续读是默认模式,在其中InnoDBzai在READ COMMITTED和REPEATABLE READ隔离级别处理SELECT语句。 持续读不在任何它访问的表上设置锁定,因此,其它用户可自由地在持续读在一个表上执行的同一时间修改这 些表。 注意,持续读不在DROP TABLE和ALTER TABLE上作用。持续读不在DROP TABLE上作用,因为MySQL不能使 用已经被移除的表,并且InnoDB 破坏了该表。持续读不在ALTER TABLE上作用,因为它在某事务内执行,该 事务创建一个新表,并且从旧表往新表中插入行。现在,当你重新发出持续读之时,它不能在新表中看见任何 行,因为它们被插入到一个在持续读读取的快照中不可见的事务里。 15.2.10.5. 锁定读SELECT ... FOR UPDATE和SELECT ... LOCK IN SHARE MODE 在一些环境中,一个持续读是不方便的。比如,你可能想要往表的子表里添加一个新行,并确信该子表在父表 中有一个根。下列例子显示如何在你应用程序代码中实现参考的完整性。 假设你使用一个持续读去读取父表并且看到表中子表的根。不能安全地往子表添加子行吗?不,因为可能同时 发生一些其它用户从父表删除父行,而你没有注意到它的情况。 解决办法是在使用LOCK IN SHARE MODE的锁定模式执行SELECT: SELECT * FROM parent WHERE NAME = 'Jones' LOCK IN SHARE MODE; 在共享模式执行一个读意味着我们读最新的可用数据,并在我们读的行设置一个共享锁定。共享模式锁防止其 它人更新或删除我们已读的行。同时,如果最新的数据属于其它客户端尚未提交的事务,我们等着知道那个事 务被提交。我们看到前述的查询返回父'Jones',我们可以安全地往子表添加子记录并提交我们的事务。 让我们看另外一个例子:我们在表child_codes 中有一个整数计数器域,我们用该表给每个添加到子表里的子 项指派一个唯一的识别符。显然,使用持续读或者共享模式读去读取当前计数器的值并是一个好主意, 因为数 据库的两个用户可能看到计数器的同一个值,如果两个用户试着用同一识别符往该表添加子项,就会发生一个 重复键(duplicate-key)错误。 在这里,如果两个用户同时读计数器,当试图更新计数器之时,至少它们中有一个会发生死锁错误并终止,因 此LOCK IN SHARE MODE并不是一个好的解决方法。 在这种情况下,有两个好方法去实现读计数器和增长计数器值:(1) 先更新计数器,让计数器值增1,之后读计 数器,或者(2)用锁定模式FOR UPDATE先读计数器,之后计数器值增加。后一个途径可被如下实现: SELECT counter_field FROM child_codes FOR UPDATE; UPDATE child_codes SET counter_field = counter_field + 1; SELECT ... FOR UPDATE读最新的可见数据,在每个它读取的行设置独占锁定。因此,它设置与搜索的SQL UPDATE可能会在行上设置的锁定同样的锁定。 请注意,以上仅是一个SELECT ... FOR UPDATE如何起作用的例子。在MySQL中,事实上生成一个唯一识别符 的特殊任务可被用对该表的单独访问来完成: UPDATE child_codes SET counter_field = LAST_INSERT_ID(counter_field + 1); SELECT LAST_INSERT_ID(); SELECT语句仅仅取回识别符信息(专门对当前连接)。它不访问任何表。 15.2.10.6. Next-Key锁定:避免幽灵问题 在行级锁定中,InnoDB 使用一个名为next-key locking的算法。InnoDB以这样一种方式执行行级锁定:当它搜 索或扫描表的索引之时,它对遇到的索引记录设置共享或独占锁定。因此,行级锁定事实上是索引记录锁定。 InnoDB对索引记录设置的锁定也映像索引记录之前的“间隙”。如果一个用户对一个索引上的记录R有共享或独 占的锁定,另一个用户不能紧接在R之前以索引的顺序插入一个新索引记录。这个间隙的锁定被执行来防止所 谓的“幽灵问题”。假设你想要从有一个标识符值大于100的子表读并锁定所有子记录,并想着随后在选定行中 更新一些列: SELECT * FROM child WHERE id > 100 FOR UPDATE; 假设在id列有一个索引。查询从id大于100的第一个记录开始扫描。如果设置在索引记录上的锁定不把在间隙生 成的插入排除在外,一个新行可能与此同时被插进表中。如果你在同一事务内执行同样的SELECT,你可能会 在该查询返回的结果包里看到一个新行。这与事务的隔离原则是相反的:一个事务应该能够运行,以便它已经 读的数据在事务过程中不改变。如果我们把一套行视为数据项,新的“幽灵”子记录可能会违反这一隔离原则。 当InnoDB扫描一个索引之时,它也锁定所以记录中最后一个记录之后的间隙。刚在前一个例子中发 生:InnoDB设置的锁定防止任何插入到id可能大过100的表。 你可以用next-key锁定在你的应用程序上实现一个唯一性检查:如果你以共享模式读数据, 并且没有看到你将 要插入的行的重复,则你可以安全地插入你的行,并且知道在读过程中对你的行的继承者设置的next-key锁定 与此同时阻止任何人对你的行插入一个重复。因此,the next-key锁定允许你锁住在你的表中并不存在的一些 东西。 15.2.10.7. 持续读如何在InnoDB中工作的例子 假设你正运行在默认的REPEATABLE READ隔离级别。当你发出一个持续读之时,即一个普通的SELECT语 句,InnoDB按照你的查询看到的数据库,给你的事务一个时间点。如果另一个事务在你的时间点被指定之后删 除一行并提交,你不会看见已被删除的行。插入和更新被相似地处理。 你可以通过提交你的事务来提前你的时间点,然后做另一个SELECT。 这被称为多版本并发控制(multi-versioned concurrency control)。 User A User B SET AUTOCOMMIT=0; SET AUTOCOMMIT=0; time | SELECT * FROM t; | empty set | INSERT INTO t VALUES (1, 2); | v SELECT * FROM t; empty set COMMIT; SELECT * FROM t; empty set COMMIT; SELECT * FROM t; --------------------- | 1 | 2 | --------------------- 1 row in set 在这个例子中,只有当用户B已经提交插入,并且用户A也已经提交之时,用户A可看见被用户B插入的行,因 此时间点是在用户B提交之前。 如果你想看数据库的最新状态,你应该用READ COMMITTED隔离级别或用一个锁定读: SELECT * FROM t LOCK IN SHARE MODE; 15.2.10.8. 在InnoDB中不同SQL语句设置的锁定 在SQL查询处理中,一个锁定读,一个UPDATE或一个DELETE一般地对被扫描的每个索引记录设置记录锁定。 如果在某查询中有一个WHERE条件是没什么关系的,而这个查询可能从查询的结果包中排除行。InnoDB不记 得确切的WHERE条件,但是仅知道哪个索引范围被扫描。记录锁定是正常的next-key锁定,它也阻止对紧接着 记录之前的间隙的插入。 如果锁定被设置为独占,则InnoDB总是取回集束的索引目录并对其设置锁定。 如果你没有适合查询的索引,MySQL不得不扫描整个表来处理查询,表的每行变成被锁定的,这样反过来阻止 其它用户的所有对表的插入。创建一个好的索引让你的查询不必要扫描很多行是很重要的。 · SELECT ... FROM是一个持续读,读取数据库的快照并且设置不锁定,除非事务隔离级别被设 为SERIALIZABLE。对于 SERIALIZABLE级别,这个设置对它遇到的索引记录设置共享的next-key锁定。 · SELECT ... FROM ... LOCK IN SHARE MODE对读遇到的所有索引记录设置共享的next-key锁定。 · SELECT ... FROM ... FOR UPDATE对读遇到的所有索引记录设置独占的next-key锁定。 · INSERT INTO ... VALUES (...)对被插入的行设置独占锁定。注意,这不是一个next-key锁定,并且不阻 止其它用户在已插入行之前的间隙插入。如果发生重复键错误,对重复的索引记录设置共享锁定。 · 在一个表上初始化之前指定的AUTO_INCREMENT列之时,InnoDB在与AUTO_INCREMENT列相关联的 索引的末尾设置独占锁定。在访问自动增长计数器中,InnoDB使用专用的表锁定模式AUTO-INC,其中锁定仅 持续到当前SQL语句的结束,而不是到整个事务的结束。请参阅15.2.10.2节,“InnoDB和AUTOCOMMIT”。 InnoDB取回先前初始化的AUTO_INCREMENT列的值而不设定任何锁定。 · INSERT INTO T SELECT ... FROM S WHERE ... 对每个插入到T的行设置独占(非next-key)锁定。它 在S上把搜索当作一个持续读,但是如果MySQL二进制日志功能被打开,它就对S设置一个共享的next-key锁 定。InnoDB在后一种情况不得不设置锁定:在从一个备份的前滚恢复中,每个SQL语句不得不以与它最初被执 行的方式完全同样的方式执行。 · CREATE TABLE ... SELECT ... 把SELECT当作一个持续读来执行,或者带着共享锁定来执行,如前面的 条目所述。 · 如果唯一键没有冲突,REPLACE象一个插入一样被做。另外,对必须更新的行设置一个独占的nextkey 锁定。 · UPDATE ... WHERE ... 对搜索遇到的每个记录设置一个独占的next-key锁定。 · DELETE FROM ... WHERE ... 对搜索遇到的每个记录设置一个独占的next-key锁定。 · 如果对一个表定义FOREIGN KEY约束,任何需要检查约束条件的插入,更新或删除对它看着检查约束 的记录设置共享行级锁定。InnoDB在约束失败的情况下也设置这些锁定。 · LOCK TABLES设置表锁定,但是是InnoDB层之上更高的MySQL层设置这些锁定。如 果innodb_table_locks=1并且 and AUTOCOMMIT=0,InnoDB意识到表锁定,并且InnoDB之上的MySQL层知 道行级锁定。另外,InooDB的自动死锁检测不能检测在这个表锁定被涉及之处的死锁。同样,既然更高 的MySQL层不知道行级锁定,很可能对另一个用户当前对其有行锁定的表获得一个表锁定。尽管如此,这并不 破坏事务的完整性,如15.2.10.10节,“死锁检测和回滚”中讨论的一样。请参阅15.2.16节,“对InnoDB表的限 制”。 15.2.10.9. MySQL何时隐式提交或回滚一个事务? MySQL以默认允许autocommit模式来开始每一个客户端连接。当autocommit被允许之时,如果SQL语句不返 回错误的话,MySQL在每个SQL语句之后,做一个提交。 如果你关闭autocommit模式并且关闭一个连接而不调用你的事务的明确提交,则MySQL回滚你的事务。 如果SQL语句返回一个错误,提交/回滚行为取决于这个错误。请参阅15.2.15节,“InnoDB错误处理”。 下列每一个语句(以及它们的任何同义词)隐式结束一个事务,就好像在执行这个语句之前你做了一 个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, UNLOCK TABLES. · UNLOCK TABLES 仅在如果任何表当前被锁定之时,提交一个事务。 · 在InnoDB中的CREATE TABLE语句被作为一个单独的事务来处理。这意味着来自用户的ROLLBACK不撤 销用户在事务过程中生成的CREATE TABLE语句。 事务不能被嵌套。当你发出START TRANSACTION语句或与之同义的语句之时,这是对任何当前事务隐式提交 的一个结果。 15.2.10.10. 死锁检测和回滚 InnoDB自动检测事务的死锁,并回滚一个或几个事务来防止死锁。InnoDB试着挑选小事务来回滚,事务的大 小通过被插入、更新或删除的行的数量来确定。 如果innodb_table_locks=1 (1是默认值),InnoDB意识到表锁定,其上的MySQL层知道row-level锁定。另 外InnoDB不能在MySQL LOCK TABLES设定表锁定的地方或者涉及InnoDB之外的存储引擎设置锁定的地方检测 死锁。你必须通过设定innodb_lock_wait_timeout系统变量的值来解决这些情况。 当InnoD执行完全的事务回滚之时,该事务的所有锁定被释放。尽管如此,如果单个SQL语句被因为错误的原 因被回滚,该SQL语句设定的部分锁定可能被保留。这是因为InnoDB以一种方式存储行锁定,在这种方式中 它不能知道随后的哪个锁定是被哪个SQL语句设定的。 15.2.10.11. 如何处理死锁 死锁是事务型数据库典型的问题,但是除非它们频繁出现以至于你更本不能运行某个事务,它们一般是不危险 的。正常地,你必须编写你的应用程序使得它们总是准备如果因为死锁而回滚一个事务就重新发出一个事务。 InnoDB使用自动行级锁定。即使在只插入或删除单个行的事务的情况下,你可以遇到死锁。这是因为这些操作 不是真正的“极小的”,它们自动对插入或删除的行的(可能是数个)索引记录设置锁定。 你可以用下列技术对付死锁减少它们发生的可能性: · 用Use SHOW INNODB STATUS来确定最后一个死锁的原因。这样可以帮助你调节应用程序来避免死 锁。 · 总是准备着重新发出事务,如果它因为死锁而失败了。死锁不危险,再试一次。 · 经常提交你的事务。小事务更少地倾向于冲突。 · 如果你正使用锁定读,(SELECT ... FOR UPDATE或 ... LOCK IN SHARE MODE),试着用更低的隔离 级别,比如READ COMMITTED。 · 以固定的顺序访问你的表和行。则事务形成良好定义的查询并且没有死锁。 · 添加精心选定的索引到你的表。则你的查询需要扫描更少的索引记录并且因此设置更少的锁定。使 用EXPLAIN SELECT来确定对于你的查询,MySQL认为哪个索引是最适当的。 · 使用更少的锁定。如果你可以接受允许一个SELECT从一个旧的快照返回数据,不要给它添加FOR UPDATE或LOCK IN SHARE MODE子句。这里使用READ COMMITTED隔离级别是比较好的,因为每个在同一 事务里的持续读从它自己新鲜的快照里读取。 · 如果没有别的有帮助的了,用表级锁定系列化你的事务。用LOCK TABLES对事务型表(如InnoDB)的正 确方法是设置AUTOCOMMIT = 0 并且不调用UNLOCK TABLES直到你明确地提交了事务。例如,如果你需要写 表t1并从表t读,你可以按如下做: · SET AUTOCOMMIT=0; · LOCK TABLES t1 WRITE, t2 READ, ...; · [do something with tables t1 and t2 here]; · COMMIT; · UNLOCK TABLES; 表级锁定使得你的事务很好地排队,并且死锁被避免了。 · 领一个系列化事务的方法是创建一个辅助的“semaphore” 表,它只包含一个单行。让每个事务在访问其 它表之前更新那个行。以这种方式,所有事务以序列的方式发生。注意,InnoDB即时死锁检测算法也能在这种 情况下起租用,因为系列化锁定是行级锁定。超时方法,用MySQL表级锁定,必须被用来解决死锁。 · 在应用程序中使用LOCK TABLES命令,如果AUTOCOMMIT=1,MySQL不设定InnoDB表锁定 InnoDB错误处理 15.2.15.1. InnoDB错误代码 15.2.15.2. 操作系统错误代码 在InnoDB中错误处理不像SQL标准中指定的一样。按照标准,在SQL语句过程中的任何错误应该导致该语句的 回滚。InnoDB有时仅回滚部分语句,或者整个事务。下列条目叙述InnoDB如何执行错误处理: · 如果你耗尽表空间中的文件空间,你使得MySQL表完全错误,并且InnoDB返回SQL语句。 · 一个事务死锁导致InnoDB回滚整个事务,在锁定等待超时的情况,InnoDB仅回滚最近的SQL语句。 当一个事务回滚因为死锁或锁定等待超时而发生,它在事务中撤销语句的作用。但是如果事务是用START TRANSACTION或BEGIN语句开始的,它就不撤销该语句。进一步,SQL语句变成事务的一部分直到COMMIT, ROLLBACK或者导致暗地提交的SQL语句发生。 · 如果你没有在语句中指定IGNORE选项,重复键错误回滚SQL语句。 · 行太长错误回滚SQL语句。 · 其它错误主要被代码的MySQL层(在InnoDB存储引擎级别以上)探测,它们回滚相应的SQL语句。在 单个SQL语句中的回滚中锁定不被释放。 在暗地回滚过程中,就像在一个明确的ROLLBACK SQL命令的执行过程中一样,SHOW PROCESSLIST在State列为有关的连接显示Rolling back。 15.2.15.1. InnoDB错误代码 下面的这个不完全列表是你可能遇见的常见的InnoDB专有错误,带着为什么发生的原因以及如何该解决问题的 相关信息 · 1005 (ER_CANT_CREATE_TABLE) 不能创建表。如果错误信息字符串指向errno 150,因为外键约束被不正确地形成,所以表创建失败。 · 1016 (ER_CANT_OPEN_FILE) 虽然对某表的.frm文件存在,但不能从InnoDB数据文件找到该InnoDB表。请参阅15.2.17.1节,“InnoDB数据 词典操作故障诊断和排除”。 · 1114 (ER_RECORD_FILE_FULL) InnoDB耗尽表空间中的可用空间,你应该重新配置表空间来添加一个新数据文件。 · 1205 (ER_LOCK_WAIT_TIMEOUT) 锁定等待超时过期。事务被回滚。 · 1213 (ER_LOCK_DEADLOCK) 事务死锁。你应该重运行事务。 · 1216 (ER_NO_REFERENCED_ROW) 你正试着添加一行,但没有父行,并且一个外键约束失败。你应该先添加父行。 · 1217 (ER_ROW_IS_REFERENCED) 你正试图删除一个有子行的父行,并且一个外键约束失败。你应该先删除子行。 15.2.15.2. 操作系统错误代码 要打印一个操作系统错误号的意思,请使用MySQL分发版里的perror程序。 下面表提供一些常用Linux系统错误代码。更完整的列表请参阅Linux source code。 · 1 (EPERM) 操作不被允许 · 2 (ENOENT) 无此文件或目录 · 3 (ESRCH) 无此进程 · 4 (EINTR) 中断的系统调用 · 5 (EIO) I/O 错误 · 6 (ENXIO) 无此设备或地址 · 7 (E2BIG) Arg列表太长 · 8 (ENOEXEC) Exec合适错误 · 9 (EBADF) 坏文件号 · 10 (ECHILD) 无子进程 · 11 (EAGAIN) 再试一次 · 12 (ENOMEM) 内存耗尽 · 13 (EACCES) 许可被否定 · 14 (EFAULT) 坏地址 · 15 (ENOTBLK) 阻止需求的设备 · 16 (EBUSY) 设备或资源忙 · 17 (EEXIST) 文件存在 · 18 (EXDEV) 交叉设备连接 · 19 (ENODEV) 无此设备 · 20 (ENOTDIR) 不是一个目录 · 21 (EISDIR) 是一个目录? · 22 (EINVAL) 非法参量 · 23 (ENFILE) 文件表溢出 · 24 (EMFILE) 打开的文件过多 · 25 (ENOTTY) 设备不适合的ioctl · 26 (ETXTBSY) 文本文件忙 · 27 (EFBIG) 文件太大 · 28 (ENOSPC) 设备上没空间了 · 29 (ESPIPE) 非法查找 · 30 (EROFS) 只读文件系统 · 31 (EMLINK) 太多连接 下列表提供一列常用Windows系统错误代码。完整列表请访问Microsoft website。 · 1 (ERROR_INVALID_FUNCTION) 不正确函数。 · 2 (ERROR_FILE_NOT_FOUND) 系统不能找到指定的文件。 · 3 (ERROR_PATH_NOT_FOUND) 系统不能找到指定的路径。 · 4 (ERROR_TOO_MANY_OPEN_FILES) 系统不能打开文件。 · 5 (ERROR_ACCESS_DENIED) 访问被拒绝。 · 6 (ERROR_INVALID_HANDLE) 句柄非法。 · 7 (ERROR_ARENA_TRASHED) 存储控制块被破坏。 · 8 (ERROR_NOT_ENOUGH_MEMORY) 无足够存储来处理这个命令。 · 9 (ERROR_INVALID_BLOCK) 存储控制块地址非法。 · 10 (ERROR_BAD_ENVIRONMENT) 环境不正确。 · 11 (ERROR_BAD_FORMAT) 试图用不正确的格式装载一个程序。 · 12 (ERROR_INVALID_ACCESS) 访问代码不合法。 · 13 (ERROR_INVALID_DATA) 数据不合法。 · 14 (ERROR_OUTOFMEMORY) 无足够的存储来完成这个操作。 · 15 (ERROR_INVALID_DRIVE) 系统不能找到指定的驱动器。 · 16 (ERROR_CURRENT_DIRECTORY) 目录不能被删除。 · 17 (ERROR_NOT_SAME_DEVICE) 系统不能移动此文件到一个不同的磁盘驱动器。 · 18 (ERROR_NO_MORE_FILES) 没有更多文件。 · 19 (ERROR_WRITE_PROTECT) 媒质写保护。 · 20 (ERROR_BAD_UNIT) 系统不能找到指定的设备。 · 21 (ERROR_NOT_READY) 设备未准备好。 · 22 (ERROR_BAD_COMMAND) 设备不能识别此命令。 · 23 (ERROR_CRC) 数据错误(循环冗余码校验). · 24 (ERROR_BAD_LENGTH) 程序发出一个命令,但是命令长度不正确。 · 25 (ERROR_SEEK) 驱动器不能在磁盘上定位指定区域或磁道。 · 26 (ERROR_NOT_DOS_DISK) 指定的磁盘或软盘不能被访问。 · 27 (ERROR_SECTOR_NOT_FOUND) 驱动器不能找到请求的扇区。 · 28 (ERROR_OUT_OF_PAPER) 打印机缺纸。 · 29 (ERROR_WRITE_FAULT) 系统不能写指定设备。 · 30 (ERROR_READ_FAULT) 系统不能从指定设备读。 · 31 (ERROR_GEN_FAILURE) 附加到系统的设备不起作用。 · 32 (ERROR_SHARING_VIOLATION) 进程不能访问文件,因为它正被另一个进程使用。 · 33 (ERROR_LOCK_VIOLATION) 进程不能访问文件,因为另一个进程已经锁定文件的一部分。 · 34 (ERROR_WRONG_DISK) 驱动器的的磁盘不正确,请插入 %2 (卷系列号: %3) 到驱动器 %1. · 36 (ERROR_SHARING_BUFFER_EXCEEDED) 太多文件被打开以共享。 · 38 (ERROR_HANDLE_EOF) 到达文件的末尾。 · 39 (ERROR_HANDLE_DISK_FULL) 磁盘已满。 · 87 (ERROR_INVALID_PARAMETER) 参数不正确。(如果你在Windows中得到这个错误,并且已经在my.cnf或my.ini文件中设 置innodb_file_per_table,则添加innodb_flush_method=unbuffered到你的my.cnf或my.ini文件)。 · 112 (ERROR_DISK_FULL) 磁盘已满。 · 123 (ERROR_INVALID_NAME) 文件名,目录名或者卷标语法不正确。 · 1450 (ERROR_NO_SYSTEM_RESOURCES) 存在系统资源不够完成请求的服务。 对InnoDB表的限制 · 一个表不能包含超过1000列。 · 内部最大键长度是3500字节,但MySQL自己限制这个到1024字节。 · 除了VARCHAR, BLOB和TEXT列,最大行长度稍微小于数据库页的一半。即,最大行长度大约8000字 节。LONGBLOB和LONGTEXT列必须小于4GB, 总的行长度,页包括BLOB和TEXT列,必须小于4GB。InnoDB在 行中存储VARCHAR,BLOB或TEXT列的前768字节,余下的存储的分散的页面中。 · 虽然InnoDB内部地支持行尺寸大于65535,你不能定义一个包含VARCHAR列的,合并尺寸大 于65535的行。 · mysql> CREATE TABLE t (a VARCHAR(8000), b VARCHAR(10000), · -> c VARCHAR(10000), d VARCHAR(10000), e VARCHAR(10000), · -> f VARCHAR(10000), g VARCHAR(10000)); · ERROR 1118 (42000): Row size too large. The maximum row size for the · used table type, not counting BLOBs, is 65535. You have to change some · columns to TEXT or BLOBs · 在一些更老的操作系统上,数据文件必须小于2GB。 · InnoDB日志文件的合并尺寸必须小于4GB。 · 最小的表空间尺寸是10MB。最大的表空间尺寸是4,000,000,000个数据库页(64TB)。这也是一个表 的最大尺寸。 · InnoDB表不支持FULLTEXT索引。 · ANALYZE TABLE 通过对每个索引树做八次随机深入并相应地更新索引集估值,这样来计数集。注意, 因为这是仅有的估值,反复运行ANALYZE TABLE会产生不同数。这使得 ANALYZE TABLE 在 InnoDB 表上很 快,不是百分百准确,因为它没有考虑所有的行。 MySQL 不仅在汇合优化中使用索引集估值。如果一些汇合没有以正确的方式优化,你可以试一下 ANALYZE TABLE 。很少有情况,ANALYZE TABLE 没有产生对你特定的表足够好的值,你可以使用 FORCE INDEX 在你 查询中来强制使用特定索引,或者设置 max_seeks_for_key 来确保MySQL在表扫描之上运行索引查找。请参 阅5.3.3节,“服务器系统变量”。请参阅A.6节,“优化器相关的问题”。 · 在Windows上,InnoDB总是内部地用小写字母存储数据库和表名字。要把数据库以二进制形式从Unix 移到Windows,或者从Windows移到Unix,你应该让所有数据库和表的名字都是小写。 · 警告: 不要在MySQL数据库内的把MySQL系统表从MyISAM转为InnoDB表!这是一个不被支持的操作。 如果你这么做了,MySQL直到你从备份恢复旧系统表,或用mysql_install_db脚本重建系统表才重启动。 · InnoDB在表内不保留行的内部计数。(因为多版本化,这可能确实有些复杂)。要处理一个SELECT COUNT(*) FROM t语句,InnoDB必须扫描表的一个索引,如果这个索引不在缓冲池中,扫描需要花一些时 间。要获得快速计数,你不得不使用一个自己创建的计数器表,并让你的应用按照它做的插入和删除来更新 它。如果你的表格不经常改变,使用MySQL查询缓存时一个好的解决方案。如果大致的行数就足够了, 则SHOW TABLE STATUS也可被使用。请参阅15.2.11节,“InnoDB性能调节提示”。 · 对于AUTO_INCREMENT列,你必须总是为表定义一个索引,并且索引必须包含AUTO_INCREMENT列。 在MyISAM表中,AUTO_INCREMENT列可能时多列索引的一部分。 · 当你重启MySQL服务器之时,InnoDB可能为一个AUTO_INCREMENT列重使用一个旧值(即,一个被赋 给一个老的已回滚的事务的值)。 · 当一个AUTO_INCREMENT列用完值,InnoDB限制一个BIGINT到-9223372036854775808以及BIGINT UNSIGNED到1。尽管如此,BIGINT值有由64位,所以注意到,如果你要一秒输入100万个行,在BIGINT到达 它上限之前,可能还需要将近30万年。用所有其它整数类型列,产生一个重复键错误。这类似于MyISAM如何 工作的,因为它主要是一般MySQL行为,并不特别关于任何存储引擎。 · DELETE FROM tbl_name不重新生成表,但取而代之地删除所有行,一个接一个地删除。 · TRUNCATE tbl_name为InnoDB而被映射到DELETE FROM tbl_name 并且不重置AUTO_INCREMENT计 数器。 · SHOW TABLE STATUS不能给出关于InnoDB表准确的统计数据,除了被表保留的物理尺寸。行计数仅 是在SQL优化中粗略的估计。 · 在MySQL 5.1中,如果innodb_table_locks=1(1是默认值) MySQL LOCK TABLES操作在每一个表上获 取两个锁定。除了在MySQL层的表锁定,它也获得一个InnoDB表锁定。旧版的MySQL不获取InnoDB表锁定, 旧行为可以通过设置innodb_table_locks=0 来选择。如果没有InnoDB表锁定被获得,即使表的一些记录被其 它事务锁定,LOCK TABLES完成。 · 所有被一个事务持有的InnoDB锁定在该事务被提交或中止之时被释放。因此在AUTOCOMMIT=1模式, 在InnoDB表上调用是没有太多意义的,因为被需求的InnoDB表锁定可能会被立即释放。 · 有时,在事务的过程中锁定更多的表可能是有用的。不幸地,MySQL中的LOCK TABLES执行一个暗地 的COMMIT和UNLOCK TABLES。LOCK TABLES的一个InnoDB变量已经被计划, 该计划在事务的中间被执行。 · 为建立复制从服务器的LOAD TABLE FROM MASTER语句对InnoDB表不起作用。一个工作区在主服务器 上更换表为MyISAM的,然后做负载,之后更换主服务器表回到InnoDB中。 · 在InnoDB中默认数据库页的大小是16KB。通过编译代码,你可以在8KB到64KB之间来设置这个值。你 不得不更新在univ.i源文件中的UNIV_PAGE_SIZE和UNIV_PAGE_SIZE_SHIFT的值。 · 在MySQL 5.1中,触发器不被级联的外键行为激活。 15.2.17. InnoDB故障诊断和排除 15.2.17.1. InnoDB数据词典操作的错误诊断和排除 · 一个总的规则是,当一个操作失败或这你怀疑有一个缺陷。你应该查看MySQL服务器的错误日志,该日 志典型地有一个有些象hostname.err这样的名字,或者在Windows上是mysql.err这样的。 · 故障诊断与排除之时,通常最好从命令提示符运行MySQL服务器,而不是从mysqld_safe包运行,或 不作为一个Windows服务来运行。你可以看mysqld打印到控制台上的内容,因此更好掌握发生了什么。 在Windows上,你必须用--console选项启动服务器将输出定向到控制台窗口 · 使用InnoDB Monitors获取关于某问题的信息。如果问题是性能相关的,或者你的服务器看起来被挂 起,你应该使用innodb_monitor来打印InnoDB内部状态的信息,如果问题是关于锁定,则使 用innodb_lock_monitor。如果问题是在表的创建或其它数据词典操作,使用innodb_table_monitor来打 印InnoDB内部数据词典的内容。 · 如果你猜测一个表被破坏,则在该表上运行CHECK TABLE。 15.2.17.1. InnoDB数据词典操作错误诊断和排除 表的一个特殊问题是MySQL服务器以.frm文件来保存数据词典信息,它被放在数据库目录,然而InnoDB也存储 信息到表空间文件里它自己的数据词典里。如果你把.frm文件移来移去;或者,如果服务器在数据词典操作的 中间崩溃,.frm文件可能结束与InnoDB内部数据词典的同步。 一个不同步的数据词典的症状是CREATE TABLE语句失败。如果发生这种情况,你应该查看服务器的错误日 志。如果日志说表已经存在于InnoDB内部数据词典当中,你在InnoDB表空间文件内有一个孤表,它没有对应 的.frm文件。错误信息看起来象如下的: InnoDB: Error: table test/parent already exists in InnoDB internal InnoDB: data dictionary. Have you deleted the .frm file InnoDB: and not used DROP TABLE? Have you used DROP DATABASE InnoDB: for InnoDB tables in MySQL version <= 3.23.43? InnoDB: See the Restrictions section of the InnoDB manual. InnoDB: You can drop the orphaned table inside InnoDB by InnoDB: creating an InnoDB table with the same name in another InnoDB: database and moving the .frm file to the current database. InnoDB: Then MySQL thinks the table exists, and DROP TABLE will InnoDB: succeed. 你可以按照错误日志里给的指示移除一个孤表。如果还是不能成功地使用DROP TABLE,问题可能是因为 在mysql客户端里的名字完成。要解决这个问题,用--disable-auto-rehash选项来启动mysql客户端并再次尝 试DROP TABLE 。(有名字完成打开着,mysql试着构建个表名字的列表,当一个正如描述的问题存在之时, 这个列表就不起作用)。 不同步数据词典的另一个“同义词”是MySQL打印一个不能打开.InnoDB文件的错误: ERROR 1016: Can't open file: 'child2.InnoDB'. (errno: 1) 在错误日志你可以发现一个类似于此的信息: InnoDB: Cannot find table test/child2 from the internal data dictionary InnoDB: of InnoDB though the .frm file for the table exists. Maybe you InnoDB: have deleted and recreated InnoDB data files but have forgotten InnoDB: to delete the corresponding .frm files of InnoDB tables? 这意味这有一个孤单的.frm文件,在InnoDB内没有相对应的表。你可以通过手动删除来移除这个孤单的.frm文 件。 如果MySQL在一个 ALTER TABLE操作的中间崩溃,你可以用InnoDB表空间内临时孤表来结束。你可以 用innodb_table_monitor看一个列出的表,名为#sql-...。如果你把表的名字包在`(backticks)里,你可以在名 字包含“#”字符的表上执行SQL语句。因此,你可以用前述的的方法象移除其它孤表一样移除这样一个孤表。 注意,要在Unix外壳里复制或重命名一个文件,如果文件名包含"#"字符,你需要把文件名放在双引号里 MySQL簇术语表 下面给出的术语对理解MySQL簇有所帮助,而且在与MySQL簇一起使用时有一定的特殊含义。 · 簇: 按照通常的理解,簇是一个计算机集合,其功能相当于1个单位,一起工作以完成单一任务。 NDB簇: 这是MySQL中使用的存储引擎,用于实现数据存储、检索、以及多个计算机间分布式管理。 MySQL簇: 指的是使用NDB存储引擎一起工作的一组计算机,以便在使用“内存中”存储的非共享体系结构中支持分布式MySQL数据库。 · 配置文件: 包含与簇、其主机和节点有关的指令和信息的文本文件。当簇启动时,这类文件由簇的管理节点负责读取。详情请参见17.4.4节,“配置文件”。 · 备份: 所有簇数据、事务和日志的完整拷贝,保存在磁盘上或其他长期存储介质上。 · 恢复: 将簇返回以前的状态,与保存在备份中的相同。 · 检查点: 从广义上讲,将数据保存到磁盘时,即达到了检查点。具体对于簇来说,它是将所有已提交事务保存到磁盘的时间点。对于NDB存储引擎,有两种一起工 作的检查点,以确保维护簇数据的一致性: o 本地检查点(LCP): 这是专门针对单一节点的检查点,但是,LCP也会在簇中的所有节点发生,同步性或强或弱。LCP包括将节点的所有数据保存到磁盘,通常每几分钟出现一 次。准确的时间间隔会出现变化,具体情况取决于节点上保存的数据量,簇活动的级别,以及其他因素。 o 全局检查点(GCP): GCP每数秒出现一次,此时,将对所有节点的事务进行同步化处理,并将redo日志保存到磁盘。 · 簇主机: 构成MySQL簇组成部分的计算机。簇具有物理结构和逻辑结构。从物理意义上讲,簇由多个计算机构成,这些计算机也称为簇主机(或主机)另请参见下 面的节点和节点组。 · 节点: 它指的是MySQL簇的逻辑或功能性单元,有时也称为簇节点。对于MySQL簇,我们使用术语“节点”表示进程而不是簇的物理部件。实施有效的MySQL簇需 要三种节点。它们是: o 管理(MGM)节点: 负责管理簇中的其他节点。它为其他节点提供了配置数据,负责启动和停止节点,处理网络分区事宜,创建备份和从备份恢复,等等。 o SQL(MySQL服务器)节点: MySQL服务器的实例,用作簇数据节点所保存数据的前端。打算保存、检索或更新数据的客户端可访问SQL节点,就像访问其他MySQL服务器一样,采用 通常的鉴定方法和API,节点组之间的基本数据分配对用户和应用程序来说是透明的。SQL节点访问作为整体的簇数据库,而不管数据在不同数据节点或簇 主机上的分布情况。 o 数据节点: 这些节点负责保存实际数据。表数据片段保存在节点组集合中,每个节点组保存表数据的不同子集。构成节点组的每个节点均保存了该节点组所负责片段 的副本。目前,1个簇能支持总数为48的数据节点。 1个以上的节点能共存于单台机器上(事实上,能够在一台机器上设置完成的簇,但在生产环境下几乎没人会这样做)。请记住,使用MySQL簇时,术 语“主机”指的是簇的物理部件,而“节点”指的是逻辑或功能部件(即进程),这很有帮助。 关于过时术语的注释:在MySQL簇文档的早期版本中,数据节点有时也称为“数据库节点”、“DB节点”、或偶尔使用的“存储节点”。此外,SQL节点有时也 称为“客户端节点”或“API节点”。为了将混淆降至最低,这类早期术语已被放弃,为此,应避免使用它们。 · 节点组: 数据节点的集合。位于节点组内的所有数据节点包含相同的数据(片段),而且单个组内的所有节点应位于不同的主机上。能够对哪个节点术语哪个节点 组进行控制。 · 节点失败: MySQL簇并不仅取决于构成构成簇的任一节点的功能,如果1个或多个节点失败,簇仍能运行。给定簇能容忍的失败节点的准确数目取决于节点的数目和簇 的配置。 · 节点重启: 重启失败簇节点的进程。 · 首次节点重启: 不与其文件系统一起启动簇节点的进程。有时用于软件升级或其他特殊场合。 · 系统崩溃(或系统失败): 当很多簇节点失败并无法保证簇的状态时,就会出现该情况。 · 系统重启: 重启簇并根据磁盘日志和检查点重新初始化簇状态的进程。在簇的计划关闭或非计划关闭后,通常需要执行该进程。 · 片段: 数据库表的一部分,在NDB存储引擎中,将表分为众多片段,并以片段方式保存。片段有时也称为“分区”,但“片段”是更可取的术语。在MySQL簇中,对 表进行了片段化处理,从而简化了机器和节点间的负载平衡。 · 副本: 在NDB存储引擎下,每个表片段均有多个副本,这些副本保存在其他数据节点上,以提供冗余性。目前,每片段最多有4个副本。 · 传输器: 提供节点间数据传输的协议。MySQL簇目前提供了四种不同的传输器连接: o TCP/IP(本地) 当然,这是广为人知的网络协议,它是Internet上HTTP、FTP等的基础。 o TCP/IP(远程) 同上,但用于远程通信。 o SCI SCI(规模可扩展的计算机连接接口)是一种高速协议,由于构建多处理器系统和并行处理应用程序。与MySQL簇一起使用SCI时,需要专门的硬件,具体 讨论请参见17.7.1节,“配置MySQL簇以使用SCI套接字”。关于SCI的基本介绍,请参见dolphinics.com上的文章。 o SHM Unix风格共享内存(SHM)段。在任何支持的场合下,将自动使用SHM来连接运行在相同主机上的节点。要想了解这方面的更多信息,请参见关 于shmop(2)的Unix手册页。 注释:簇传输器是簇内部的。使用MySQL簇的应用程序与SQL节点进行通信,就像与其他版本的MySQL服务器进行通信一样(通过TCP/IP、或使用Unix套 接字、或Windows命名管道)。可以使用标准的MySQL API发出查询并检索结果。 · NDB: 它是网络数据库(Network Database)的缩写,而且指的是用于启用MySQL簇的存储引擎。NDB存储引擎支持所有通常的MySQL列类型和SQL语句,而且 是ACID兼容的。该引擎还提供了对事务的完整支持(提交和回滚)。 · 无共享体系结构: 用于MySQL簇的理想体系结构。在真正的无共享设置下,每个节点运行在单独的主机上。这类安排的优点在于,单个主机或节点不会造成单点故障,也不 会成为系统整体的性能瓶颈。 · 内存中存储: 保存在每个数据节点中的数据均保存在节点宿主计算机的内存中。对于簇中的每个数据节点,必须提供足够的RAM,所需的RAM量为:数据库的大小乘以 副本数目,然后除以数据节点的数目。因此,如果数据库占用1GB的内存,而且你打算设置具有4个副本和8个数据节点的簇,每节点所需的最小内存为500 MB。注意,还应加上操作系统所需的内存以及运行在主机上的应用程序所需的内存。 · 表: 与关联数据库下的通常情况一样,术语“表”指的是具有等同结构的记录的集合。在MySQL簇中,数据库表以片段集合的方式保存在数据节点中,并将每个 片段复制到额外的数据节点上。复制相同片段或片段集合的数据节点集合称为节点组。 · 簇程序: 它们是命令行程序,用于运行、配置和管理MySQL簇。它们包括两种服务器端口监督程序: o ndbd: 数据节点端口监督程序(运行数据节点进程)。 o ndb_mgmd: 管理服务器端口监督程序(运行管理服务器进程)。 以及客户端程序: o ndb_mgm: 管理客户端(为执行管理命令提供了1个界面)。 o ndb_waiter: 用于验证簇中所有节点的状态。 o ndb_restore: 从备份中恢复簇数据。 关于这些程序及其用法的更多信息,请参见17.5节,“MySQL簇中的进程管理”。 · 事件日志: MySQL簇按照类别(启动、停止、错误、检查点等)、优先级和严重级别来记录事件。关于所有可通报事件的完整列表,请参见17.6.3节,“MySQL簇中生 成的事件报告”。事件日志具有两种类型: o 簇日志: 为作为整体的簇记录所有所需的、值得记录的事件。 o 节点日志: 为每个单独节点保存的单独日志。 在正常情况下,仅需保存和检查簇日志,这通常已足够。节点日志仅用于应用程序开发和调试目的。 分区类型 18.2.1. RANGE分区 18.2.2. LIST分区 18.2.3. HASH分区 18.2.4. KEY分区 18.2.5. 子分区 18.2.6. MySQL分区处理NULL值的方式 本节讨论在MySQL 5.1中可用的分区类型。这些类型包括: · RANGE 分区:基于属于一个给定连续区间的列值,把多行分配给分区。参见18.2.1节,“RANGE分 区”。 · LIST 分区:类似于按RANGE分区,区别在于LIST分区是基于列值匹配一个离散值集合中的某个值来进 行选择。参见18.2.2节,“LIST分区”。 · HASH分区:基于用户定义的表达式的返回值来进行选择的分区,该表达式使用将要插入到表中的这些 行的列值进行计算。这个函数可以包含MySQL 中有效的、产生非负整数值的任何表达式。参 见18.2.3节,“HASH分区”。 · KEY 分区:类似于按HASH分区,区别在于KEY分区只支持计算一列或多列,且MySQL 服务器提供其自 身的哈希函数。必须有一列或多列包含整数值。参见18.2.4节,“KEY分区”。 无论使用何种类型的分区,分区总是在创建时就自动的顺序编号,且从0开始记录,记住这一点非常重要。当 有一新行插入到一个分区表中时,就是使用这些分区编号来识别正确的分区。例如,如果你的表使用4个分 区,那么这些分区就编号为0, 1, 2, 和3。对于RANGE和LIST分区类型,确认每个分区编号都定义了一个分区, 很有必要。对HASH分区,使用的用户函数必须返回一个大于0的整数值。对于KEY分区,这个问题通 过MySQL服务器内部使用的哈希函数自动进行处理。 分区的名字基本上遵循其他MySQL 标识符应当遵循的原则,例如用于表和数据库名字的标识符。但是应当注 意,分区的名字是不区分大小写的。例如,下面的CREATE TABLE语句将会产生如下的错误: mysql> CREATE TABLE t2 (val INT) -> PARTITION BY LIST(val)( -> PARTITION mypart VALUES IN (1,3,5), -> PARTITION MyPart VALUES IN (2,4,6) -> ); 错误1488 (HY000): 表的所有分区必须有唯一的名字。 这是因为MySQL认为分区名字mypart和MyPart没有区别。 注释:在下面的章节中,我们没有必要提供可以用来创建每种分区类型语法的所有可能形式,这些信息可以 在13.1.5节,“CREATE TABLE语法” 中找到。 18.2.1. RANGE分区 按照RANGE分区的表是通过如下一种方式进行分区的,每个分区包含那些分区表达式的值位于一个给定的连续 区间内的行。这些区间要连续且不能相互重叠,使用VALUES LESS THAN操作符来进行定义。在下面的几个例 子中,假定你创建了一个如下的一个表,该表保存有20家音像店的职员记录,这20家音像店的编号从1到20。 CREATE TABLE employees ( id INT NOT NULL, fname VARCHAR(30), lname VARCHAR(30), hired DATE NOT NULL DEFAULT '1970-01-01', separated DATE NOT NULL DEFAULT '9999-12-31', job_code INT NOT NULL, store_id INT NOT NULL ); 根据你的需要,这个表可以有多种方式来按照区间进行分区。一种方式是使用store_id 列。例如,你可能决定 通过添加一个PARTITION BY RANGE子句把这个表分割成4个区间,如下所示: CREATE TABLE employees ( id INT NOT NULL, fname VARCHAR(30), lname VARCHAR(30), hired DATE NOT NULL DEFAULT '1970-01-01', separated DATE NOT NULL DEFAULT '9999-12-31', job_code INT NOT NULL, store_id INT NOT NULL ) PARTITION BY RANGE (store_id) ( PARTITION p0 VALUES LESS THAN (6), PARTITION p1 VALUES LESS THAN (11), PARTITION p2 VALUES LESS THAN (16), PARTITION p3 VALUES LESS THAN (21) ); 按照这种分区方案,在商店1到5工作的雇员相对应的所有行被保存在分区P0中,商店6到10的雇员保存 在P1中,依次类推。注意,每个分区都是按顺序进行定义,从最低到最高。这是PARTITION BY RANGE 语法 的要求;在这点上,它类似于C或Java中的“switch ... case”语句。 对于包含数据(72, 'Michael', 'Widenius', '1998-06-25', NULL, 13)的一个新行,可以很容易地确定它将插入 到p2分区中,但是如果增加了一个编号为第21的商店,将会发生什么呢?在这种方案下,由于没有规则 把store_id大于20的商店包含在内,服务器将不知道把该行保存在何处,将会导致错误。要避免这种错误,可 以通过在CREATE TABLE语句中使用一个“catchall” VALUES LESS THAN子句,该子句提供给所有大于明确指定 的最高值的值: CREATE TABLE employees ( id INT NOT NULL, fname VARCHAR(30), lname VARCHAR(30), hired DATE NOT NULL DEFAULT '1970-01-01', separated DATE NOT NULL DEFAULT '9999-12-31', job_code INT NOT NULL, store_id INT NOT NULL ) PARTITION BY RANGE (store_id) ( PARTITION p0 VALUES LESS THAN (6), PARTITION p1 VALUES LESS THAN (11), PARTITION p2 VALUES LESS THAN (16), PARTITION p3 VALUES LESS THAN MAXVALUE ); MAXVALUE 表示最大的可能的整数值。现在,store_id 列值大于或等于16(定义了的最高值)的所有行都将保 存在分区p3中。在将来的某个时候,当商店数已经增长到25, 30, 或更多,可以使用ALTER TABLE语句为商 店21-25, 26-30,等等增加新的分区(关于如何实现的详细信息参见18.3节,“分区管理” )。 在几乎一样的结构中,你还可以基于雇员的工作代码来分割表,也就是说,基于job_code 列值的连续区间。 例如——假定2位数字的工作代码用来表示普通(店内的)工人,三个数字代码表示办公室和支持人员,四个 数字代码表示管理层,你可以使用下面的语句创建该分区表: CREATE TABLE employees ( id INT NOT NULL, fname VARCHAR(30), lname VARCHAR(30), hired DATE NOT NULL DEFAULT '1970-01-01', separated DATE NOT NULL DEFAULT '9999-12-31', job_code INT NOT NULL, store_id INT NOT NULL ) PARTITION BY RANGE (job_code) ( PARTITION p0 VALUES LESS THAN (100), PARTITION p1 VALUES LESS THAN (1000), PARTITION p2 VALUES LESS THAN (10000) ); 在这个例子中, 店内工人相关的所有行将保存在分区p0中,办公室和支持人员相关的所有行保存在分区p1中, 管理层相关的所有行保存在分区p2中。 在VALUES LESS THAN 子句中使用一个表达式也是可能的。这里最值得注意的限制是MySQL 必须能够计算表 达式的返回值作为LESS THAN (<)比较的一部分;因此,表达式的值不能为NULL 。由于这个原因,雇员表 的hired, separated, job_code,和store_id列已经被定义为非空(NOT NULL)。 除了可以根据商店编号分割表数据外,你还可以使用一个基于两个DATE (日期)中的一个的表达式来分割表 数据。例如,假定你想基于每个雇员离开公司的年份来分割表,也就是说,YEAR(separated)的值。实现这种 分区模式的CREATE TABLE 语句的一个例子如下所示: CREATE TABLE employees ( id INT NOT NULL, fname VARCHAR(30), lname VARCHAR(30), hired DATE NOT NULL DEFAULT '1970-01-01', separated DATE NOT NULL DEFAULT '9999-12-31', job_code INT, store_id INT ) PARTITION BY RANGE (YEAR(separated)) ( PARTITION p0 VALUES LESS THAN (1991), PARTITION p1 VALUES LESS THAN (1996), PARTITION p2 VALUES LESS THAN (2001), PARTITION p3 VALUES LESS THAN MAXVALUE ); 在这个方案中,在1991年前雇佣的所有雇员的记录保存在分区p0中,1991年到1995年期间雇佣的所有雇员的 记录保存在分区p1中, 1996年到2000年期间雇佣的所有雇员的记录保存在分区p2中,2000年后雇佣的所有工 人的信息保存在p3中。 RANGE分区在如下场合特别有用: · 当需要删除“旧的”数据时。如果你使用上面最近的那个例子给出的分区方案,你只需简单地使用 “ALTER TABLE employees DROP PARTITION p0;”来删除所有在1991年前就已经停止工作的雇员相对应的所 有行。(更多信息请参见13.1.2节,“ALTER TABLE语法” 和 18.3节,“分区管理”)。对于有大量行的表,这比 运行一个如“DELETE FROM employees WHERE YEAR(separated) <= 1990;”这样的一个DELETE查询要有效 得多。 · 想要使用一个包含有日期或时间值,或包含有从一些其他级数开始增长的值的列。 · 经常运行直接依赖于用于分割表的列的查询。例如,当执行一个如“SELECT COUNT(*) FROM employees WHERE YEAR(separated) = 2000 GROUP BY store_id;”这样的查询时,MySQL可以很迅速地确定 只有分区p2需要扫描,这是因为余下的分区不可能包含有符合该WHERE子句的任何记录。注释:这种优化还 没有在MySQL 5.1源程序中启用,但是,有关工作正在进行中。 18.2.2. LIST分区 MySQL中的LIST分区在很多方面类似于RANGE分区。和按照RANGE分区一样,每个分区必须明确定义。它们 的主要区别在于,LIST分区中每个分区的定义和选择是基于某列的值从属于一个值列表集中的一个值, 而RANGE分区是从属于一个连续区间值的集合。LIST分区通过使用“PARTITION BY LIST(expr)”来实现,其 中“expr” 是某列值或一个基于某个列值、并返回一个整数值的表达式,然后通过“VALUES IN (value_list)”的方 式来定义每个分区,其中“value_list”是一个通过逗号分隔的整数列表。 注释:在MySQL 5.1中,当使用LIST分区时,有可能只能匹配整数列表。 不像按照RANGE定义分区的情形,LIST分区不必声明任何特定的顺序。关于LIST分区更详细的语法信息,请参 考13.1.5节,“CREATE TABLE语法” 。 对于下面给出的例子,我们假定将要被分区的表的基本定义是通过下面的“CREATE TABLE”语句提供的: CREATE TABLE employees ( id INT NOT NULL, fname VARCHAR(30), lname VARCHAR(30), hired DATE NOT NULL DEFAULT '1970-01-01', separated DATE NOT NULL DEFAULT '9999-12-31', job_code INT, store_id INT ); (这和18.2.1节,“RANGE分区” 中的例子中使用的是同一个表)。 假定有20个音像店,分布在4个有经销权的地区,如下表所示: 地区商店ID 号 北区3, 5, 6, 9, 17 东区1, 2, 10, 11, 19, 20 西区4, 12, 13, 14, 18 中心区7, 8, 15, 16 要按照属于同一个地区商店的行保存在同一个分区中的方式来分割表,可以使用下面的“CREATE TABLE”语 句: CREATE TABLE employees ( id INT NOT NULL, fname VARCHAR(30), lname VARCHAR(30), hired DATE NOT NULL DEFAULT '1970-01-01', separated DATE NOT NULL DEFAULT '9999-12-31', job_code INT, store_id INT ) PARTITION BY LIST(store_id) PARTITION pNorth VALUES IN (3,5,6,9,17), PARTITION pEast VALUES IN (1,2,10,11,19,20), PARTITION pWest VALUES IN (4,12,13,14,18), PARTITION pCentral VALUES IN (7,8,15,16) ); 这使得在表中增加或删除指定地区的雇员记录变得容易起来。例如,假定西区的所有音像店都卖给了其他公 司。那么与在西区音像店工作雇员相关的所有记录(行)可以使用查询“ALTER TABLE employees DROP PARTITION pWest;”来进行删除,它与具有同样作用的DELETE (删除)查询“DELETE query DELETE FROM employees WHERE store_id IN (4,12,13,14,18);”比起来,要有效得多。 要点:如果试图插入列值(或分区表达式的返回值)不在分区值列表中的一行时,那么“INSERT”查询将失败并 报错。例如,假定LIST分区的采用上面的方案,下面的查询将失败: INSERT INTO employees VALUES (224, 'Linus', 'Torvalds', '2002-05-01', '2004-10-12', 42, 21); 这是因为“store_id”列值21不能在用于定义分区pNorth, pEast, pWest,或pCentral的值列表中找到。要重点注意 的是,LIST分区没有类似如“VALUES LESS THAN MAXVALUE”这样的包含其他值在内的定义。将要匹配的任何 值都必须在值列表中找到。 LIST分区除了能和RANGE分区结合起来生成一个复合的子分区,与HASH和KEY分区结合起来生成复合的子分 区也是可能的。关于这方面的讨论,请参考18.2.5节,“子分区”。 18.2.3. HASH分区 18.2.3.1. LINEAR HASH分区 HASH分区主要用来确保数据在预先确定数目的分区中平均分布。在RANGE和LIST分区中,必须明确指定一个 给定的列值或列值集合应该保存在哪个分区中;而在HASH分区中,MySQL 自动完成这些工作,你所要做的只 是基于将要被哈希的列值指定一个列值或表达式,以及指定被分区的表将要被分割成的分区数量。 要使用HASH分区来分割一个表,要在CREATE TABLE 语句上添加一个“PARTITION BY HASH (expr)”子句,其 中“expr”是一个返回一个整数的表达式。它可以仅仅是字段类型为MySQL 整型的一列的名字。此外,你很可能 需要在后面再添加一个“PARTITIONS num”子句,其中num 是一个非负的整数,它表示表将要被分割成分区的 数量。 例如,下面的语句创建了一个使用基于“store_id”列进行哈希处理的表,该表被分成了4个分区: CREATE TABLE employees ( id INT NOT NULL, fname VARCHAR(30), lname VARCHAR(30), hired DATE NOT NULL DEFAULT '1970-01-01', separated DATE NOT NULL DEFAULT '9999-12-31', job_code INT, store_id INT ) PARTITION BY HASH(store_id) PARTITIONS 4; 如果没有包括一个PARTITIONS子句,那么分区的数量将默认为1。例外: 对于NDB Cluster(簇)表,默认的 分区数量将与簇数据节点的数量相同,这种修正可能是考虑任何MAX_ROWS 设置,以便确保所有的行都能合 适地插入到分区中。(参见第17章:MySQL簇)。 如果在关键字“PARTITIONS”后面没有加上分区的数量,将会出现语法错误。 “expr”还可以是一个返回一个整数的SQL表达式。例如,也许你想基于雇用雇员的年份来进行分区。这可以通 过下面的语句来实现: CREATE TABLE employees ( id INT NOT NULL, fname VARCHAR(30), lname VARCHAR(30), hired DATE NOT NULL DEFAULT '1970-01-01', separated DATE NOT NULL DEFAULT '9999-12-31', job_code INT, store_id INT ) PARTITION BY HASH(YEAR(hired)) PARTITIONS 4; “expr”还可以是MySQL 中有效的任何函数或其他表达式,只要它们返回一个既非常数、也非随机数的整数。 (换句话说,它既是变化的但又是确定的)。但是应当记住,每当插入或更新(或者可能删除)一行,这个表 达式都要计算一次;这意味着非常复杂的表达式可能会引起性能问题,尤其是在执行同时影响大量行的运算 (例如批量插入)的时候。 最有效率的哈希函数是只对单个表列进行计算,并且它的值随列值进行一致地增大或减小,因为这考虑了在分 区范围上的“修剪”。也就是说,表达式值和它所基于的列的值变化越接近,MySQL就可以越有效地使用该表达 式来进行HASH分区。 例如,“date_col” 是一个DATE(日期)类型的列,那么表达式TO_DAYS(date_col)就可以说是随 列“date_col”值的变化而发生直接的变化,因为列“date_col”值的每个变化,表达式的值也将发生与之一致的变 化。而表达式YEAR(date_col)的变化就没有表达式TO_DAYS(date_col)那么直接,因为不是列“date_col”每次可 能的改变都能使表达式YEAR(date_col)发生同等的改变。即便如此,表达式YEAR(date_col)也还是一个用于哈 希函数的、好的候选表达式,因为它随列date_col的一部分发生直接变化,并且列date_col的变化不可能引起 表达式YEAR(date_col)不成比例的变化。 作为对照,假定有一个类型为整型(INT)的、列名为“int_col”的列。现在考虑表达式“POW(5-int_col,3) + 6”。这对于哈希函数就是一个不好的选择,因为“int_col”值的变化并不能保证表达式产生成比例的变化。列 “int_col”的值发生一个给定数目的变化,可能会引起表达式的值产生一个很大不同的变化。例如,把 列“int_col”的值从5变为6,表达式的值将产生“-1”的改变,但是把列“int_col”的值从6变为7时,表达式的值将 产生“-7”的变化。 换句话说,如果列值与表达式值之比的曲线图越接近由等式“y=nx(其中n为非零的常数)描绘出的直线,则该 表达式越适合于哈希。这是因为,表达式的非线性越严重,分区中数据产生非均衡分布的趋势也将越严重。 理论上讲,对于涉及到多列的表达式,“修剪(pruning)”也是可能的,但是要确定哪些适于哈希是非常困难 和耗时的。基于这个原因,实际上不推荐使用涉及到多列的哈希表达式。 当使用了“PARTITION BY HASH”时,MySQL将基于用户函数结果的模数来确定使用哪个编号的分区。换句话, 对于一个表达式“expr”,将要保存记录的分区编号为N ,其中“N = MOD(expr, num)”。例如,假定表t1 定义 如下,它有4个分区: CREATE TABLE t1 (col1 INT, col2 CHAR(5), col3 DATE) PARTITION BY HASH( YEAR(col3) ) PARTITIONS 4; 如果插入一个col3列值为'2005-09-15'的记录到表t1中,那么保存该条记录的分区确定如下: MOD(YEAR('2005-09-01'),4) = MOD(2005,4) = 1 MySQL 5.1 还支持一个被称为“linear hashing(线性哈希功能)”的变量,它使用一个更加复杂的算法来确定新 行插入到已经分区了的表中的位置。关于这种算法的描述,请参见18.2.3.1节,“LINEAR HASH分区” 。 每当插入或更新一条记录,用户函数都要计算一次。当删除记录时,用户函数也可能要进行计算,这取决于所 处的环境。 注释:如果将要分区的表有一个唯一的键,那么用来作为HASH用户函数的自变数或者主键的column_list的自 变数的任意列都必须是那个键的一部分。 18.2.3.1. LINEAR HASH分区 MySQL还支持线性哈希功能,它与常规哈希的区别在于,线性哈希功能使用的一个线性的2的幂(powers-oftwo) 运算法则,而常规哈希使用的是求哈希函数值的模数。 线性哈希分区和常规哈希分区在语法上的唯一区别在于,在“PARTITION BY” 子句中添加“LINEAR”关键字,如 下面所示: CREATE TABLE employees ( id INT NOT NULL, fname VARCHAR(30), lname VARCHAR(30), hired DATE NOT NULL DEFAULT '1970-01-01', separated DATE NOT NULL DEFAULT '9999-12-31', job_code INT, store_id INT ) PARTITION BY LINEAR HASH(YEAR(hired)) PARTITIONS 4; 假设一个表达式expr, 当使用线性哈希功能时,记录将要保存到的分区是num 个分区中的分区N,其中N是根据 下面的算法得到: 1. 找到下一个大于num.的、2的幂,我们把这个值称为V ,它可以通过下面的公式得到: 2. V = POWER(2, CEILING(LOG(2, num))) (例如,假定num是13。那么LOG(2,13)就是3.7004397181411。CEILING(3.7004397181411)就是4,则V = POWER(2,4), 即等于16)。 3. 设置 N = F(column_list) & (V - 1). 4. 当 N >= num: · 设置 V = CEIL(V / 2) · 设置 N = N & (V - 1) 例如,假设表t1,使用线性哈希分区且有4个分区,是通过下面的语句创建的: CREATE TABLE t1 (col1 INT, col2 CHAR(5), col3 DATE) PARTITION BY LINEAR HASH( YEAR(col3) ) PARTITIONS 6; 现在假设要插入两行记录到表t1中,其中一条记录col3列值为'2003-04-14',另一条记录col3列值为'1998-10- 19'。第一条记录将要保存到的分区确定如下: V = POWER(2, CEILING(LOG(2,7))) = 8 N = YEAR('2003-04-14') & (8 - 1) = 2003 & 7 = 3 (3 >= 6 为假(FALSE): 记录将被保存到#3号分区中) 第二条记录将要保存到的分区序号计算如下: V = 8 N = YEAR('1998-10-19') & (8-1) = 1998 & 7 = 6 (6 >= 4 为真(TRUE): 还需要附加的步骤) N = 6 & CEILING(5 / 2) = 6 & 3 = 2 (2 >= 4 为假(FALSE): 记录将被保存到#2分区中) 按照线性哈希分区的优点在于增加、删除、合并和拆分分区将变得更加快捷,有利于处理含有极其大量 (1000吉)数据的表。它的缺点在于,与使用常规HASH分区得到的数据分布相比,各个分区间数据的分布不 大可能均衡。 18.2.4. KEY分区 按照KEY进行分区类似于按照HASH分区,除了HASH分区使用的用户定义的表达式,而KEY分区的哈希函数是 由MySQL 服务器提供。MySQL 簇(Cluster)使用函数MD5()来实现KEY分区;对于使用其他存储引擎的表, 服务器使用其自己内部的哈希函数,这些函数是基于与PASSWORD()一样的运算法则。 “CREATE TABLE ... PARTITION BY KEY”的语法规则类似于创建一个通过HASH分区的表的规则。它们唯一的 区别在于使用的关键字是KEY而不是HASH,并且KEY分区只采用一个或多个列名的一个列表。 通过线性KEY分割一个表也是可能的。下面是一个简单的例子: CREATE TABLE tk ( col1 INT NOT NULL, col2 CHAR(5), col3 DATE ) PARTITION BY LINEAR KEY (col1) PARTITIONS 3; 在KEY分区中使用关键字LINEAR和在HASH分区中使用具有同样的作用,分区的编号是通过2的幂(powers-of two)算法得到,而不是通过模数算法。关于该算法及其蕴涵式的描述请参考18.2.3.1节,“LINEAR HASH分 区” 。 18.2.5. 子分区 子分区是分区表中每个分区的再次分割。例如,考虑下面的CREATE TABLE 语句: CREATE TABLE ts (id INT, purchased DATE) PARTITION BY RANGE(YEAR(purchased)) SUBPARTITION BY HASH(TO_DAYS(purchased)) SUBPARTITIONS 2 ( PARTITION p0 VALUES LESS THAN (1990), PARTITION p1 VALUES LESS THAN (2000), PARTITION p2 VALUES LESS THAN MAXVALUE ); 表ts 有3个RANGE分区。这3个分区中的每一个分区——p0, p1, 和p2 ——又被进一步分成了2个子分区。实际 上,整个表被分成了3 * 2 = 6个分区。但是,由于PARTITION BY RANGE子句的作用,这些分区的头2个只保 存“purchased”列中值小于1990的那些记录。 在MySQL 5.1中,对于已经通过RANGE或LIST分区了的表再进行子分区是可能的。子分区既可以使用HASH希 分区,也可以使用KEY分区。这也被称为复合分区(composite partitioning)。 为了对个别的子分区指定选项,使用SUBPARTITION 子句来明确定义子分区也是可能的。例如,创建在前面例 子中给出的同一个表的、一个更加详细的方式如下: CREATE TABLE ts (id INT, purchased DATE) PARTITION BY RANGE(YEAR(purchased)) SUBPARTITION BY HASH(TO_DAYS(purchased)) ( PARTITION p0 VALUES LESS THAN (1990) ( SUBPARTITION s0, SUBPARTITION s1 ), PARTITION p1 VALUES LESS THAN (2000) ( SUBPARTITION s2, SUBPARTITION s3 ), PARTITION p2 VALUES LESS THAN MAXVALUE ( SUBPARTITION s4, SUBPARTITION s5 ) ); 几点要注意的语法项: · 每个分区必须有相同数量的子分区。 · 如果在一个分区表上的任何分区上使用SUBPARTITION 来明确定义任何子分区,那么就必须定义所有 的子分区。换句话说,下面的语句将执行失败: · CREATE TABLE ts (id INT, purchased DATE) · PARTITION BY RANGE(YEAR(purchased)) · SUBPARTITION BY HASH(TO_DAYS(purchased)) · ( · PARTITION p0 VALUES LESS THAN (1990) · ( · SUBPARTITION s0, · SUBPARTITION s1 · ), · PARTITION p1 VALUES LESS THAN (2000), · PARTITION p2 VALUES LESS THAN MAXVALUE · ( · SUBPARTITION s2, · SUBPARTITION s3 · ) · ); 即便这个语句包含了一个SUBPARTITIONS 2子句,但是它仍然会执行失败。 · 每个SUBPARTITION 子句必须包括 (至少)子分区的一个名字。否则,你可能要对该子分区设置任何你 所需要的选项,或者允许该子分区对那些选项采用其默认的设置。 · 在每个分区内,子分区的名字必须是唯一的,但是在整个表中,没有必要保持唯一。例如,下面 的CREATE TABLE 语句是有效的: · CREATE TABLE ts (id INT, purchased DATE) · PARTITION BY RANGE(YEAR(purchased)) · SUBPARTITION BY HASH(TO_DAYS(purchased)) · ( · PARTITION p0 VALUES LESS THAN (1990) · ( · SUBPARTITION s0, · SUBPARTITION s1 · ), · PARTITION p1 VALUES LESS THAN (2000) · ( · SUBPARTITION s0, · SUBPARTITION s1 · ), · PARTITION p2 VALUES LESS THAN MAXVALUE · ( · SUBPARTITION s0, · SUBPARTITION s1 · ) · ); 子分区可以用于特别大的表,在多个磁盘间分配数据和索引。假设有6个磁盘,分别为/disk0, /disk1, /disk2等。现在考虑下面的例子: CREATE TABLE ts (id INT, purchased DATE) PARTITION BY RANGE(YEAR(purchased)) SUBPARTITION BY HASH(TO_DAYS(purchased)) ( PARTITION p0 VALUES LESS THAN (1990) ( SUBPARTITION s0 DATA DIRECTORY = '/disk0/data' INDEX DIRECTORY = '/disk0/idx', SUBPARTITION s1 DATA DIRECTORY = '/disk1/data' INDEX DIRECTORY = '/disk1/idx' ), PARTITION p1 VALUES LESS THAN (2000) ( SUBPARTITION s0 DATA DIRECTORY = '/disk2/data' INDEX DIRECTORY = '/disk2/idx', SUBPARTITION s1 DATA DIRECTORY = '/disk3/data' INDEX DIRECTORY = '/disk3/idx' ), PARTITION p2 VALUES LESS THAN MAXVALUE ( SUBPARTITION s0 DATA DIRECTORY = '/disk4/data' INDEX DIRECTORY = '/disk4/idx', SUBPARTITION s1 DATA DIRECTORY = '/disk5/data' INDEX DIRECTORY = '/disk5/idx' ) ); 在这个例子中,每个RANGE分区的数据和索引都使用一个单独的磁盘。还可能有许多其他的变化;下面是另外 一个可能的例子: CREATE TABLE ts (id INT, purchased DATE) PARTITION BY RANGE(YEAR(purchased)) SUBPARTITION BY HASH(TO_DAYS(purchased)) ( PARTITION p0 VALUES LESS THAN (1990) ( SUBPARTITION s0a DATA DIRECTORY = '/disk0' INDEX DIRECTORY = '/disk1', SUBPARTITION s0b DATA DIRECTORY = '/disk2' INDEX DIRECTORY = '/disk3' ), PARTITION p1 VALUES LESS THAN (2000) ( SUBPARTITION s1a DATA DIRECTORY = '/disk4/data' INDEX DIRECTORY = '/disk4/idx', SUBPARTITION s1b DATA DIRECTORY = '/disk5/data' INDEX DIRECTORY = '/disk5/idx' ), PARTITION p2 VALUES LESS THAN MAXVALUE ( SUBPARTITION s2a, SUBPARTITION s2b ) ); 在这个例子中,存储的分配如下: · 购买日期在1990年前的记录占了大量的存储空间,所以把它分为了四个部分进行存储,组成p0分区 的两个子分区(s0a 和s0b)的数据和索引都分别用一个单独的磁盘进行存储。换句话说: o 子分区s0a 的数据保存在磁盘/disk0中。 o 子分区s0a 的索引保存在磁盘/disk1中。 o 子分区s0b 的数据保存在磁盘/disk2中。 o 子分区s0b 的索引保存在磁盘/disk3中。 · 保存购买日期从1990年到1999年间的记录(分区p1)不需要保存购买日期在1990年之前的记录那么大 的存储空间。这些记录分在2个磁盘(/disk4和/disk5)上保存,而不是4个磁盘: o 属于分区p1的第一个子分区(s1a)的数据和索引保存在磁盘/disk4上 — 其中数据保存在路 径/disk4/data下,索引保存在/disk4/idx下。 o 属于分区p1的第二个子分区(s1b)的数据和索引保存在磁盘/disk5上 — 其中数据保存在路 径/disk5/data下,索引保存在/disk5/idx下。 · 保存购买日期从2000年到现在的记录(分区p2)不需要前面两个RANGE分区那么大的空间。当前,在 默认的位置能够足够保存所有这些记录。 将来,如果从2000年开始后十年购买的数量已经达到了默认的位置不能够提供足够的保存空间时,相应的记录 (行)可以通过使用“ALTER TABLE ... REORGANIZE PARTITION”语句移动到其他的位置。关于如何实现的说 明,请参见18.3节,“分区管理” 。 MySQL分区处理NULL值的方式 MySQL 中的分区在禁止空值(NULL)上没有进行处理,无论它是一个列值还是一个用户定义表达式的值。一 般而言,在这种情况下MySQL 把NULL视为0。如果你希望回避这种做法,你应该在设计表时不允许空值;最可 能的方法是,通过声明列“NOT NULL”来实现这一点。 在本节中,我们提供了一些例子,来说明当决定一个行应该保存到哪个分区时,MySQL 是如何处理NULL值 的。 如果插入一行到按照RANGE或LIST分区的表,该行用来确定分区的列值为NULL,分区将把该NULL值视为0。 例如,考虑下面的两个表,表的创建和插入记录如下: mysql> CREATE TABLE tnlist ( -> id INT, -> name VARCHAR(5) -> ) -> PARTITION BY LIST(id) ( -> PARTITION p1 VALUES IN (0), -> PARTITION p2 VALUES IN (1) -> ); Query OK, 0 rows affected (0.09 sec) mysql> CREATE TABLE tnrange ( -> id INT, -> name VARCHAR(5) -> ) -> PARTITION BY RANGE(id) ( -> PARTITION p1 VALUES LESS THAN (1), -> PARTITION p2 VALUES LESS THAN MAXVALUE -> ); Query OK, 0 rows affected (0.09 sec) mysql> INSERT INTO tnlist VALUES (NULL, 'bob'); Query OK, 1 row affected (0.00 sec) mysql> INSERT INTO tnrange VALUES (NULL, 'jim'); Query OK, 1 row affected (0.00 sec) mysql> SELECT * FROM tnlist; +------+------+ | id | name | +------+------+ | NULL | bob | +------+------+ 1 row in set (0.00 sec) mysql> SELECT * FROM tnrange; +------+------+ | id | name | +------+------+ | NULL | jim | +------+------+ 1 row in set (0.00 sec) 在两个表中,id列没有声明为“NOT NULL”,这意味着它们允许Null值。可以通过删除这些分区,然后重新运 行SELECT 语句,来验证这些行被保存在每个表的p1分区中: mysql> ALTER TABLE tnlist DROP PARTITION p1; Query OK, 0 rows affected (0.16 sec) mysql> ALTER TABLE tnrange DROP PARTITION p1; Query OK, 0 rows affected (0.16 sec) mysql> SELECT * FROM tnlist; Empty set (0.00 sec) mysql> SELECT * FROM tnrange; Empty set (0.00 sec) 在按HASH和KEY分区的情况下,任何产生NULL值的表达式都视同好像它的返回值为0。我们可以通过先创建一 个按HASH分区的表,然后插入一个包含有适当值的记录,再检查对文件系统的作用,来验证这一点。假定有 使用下面的语句在测试数据库中创建了一个表tnhash: CREATE TABLE tnhash ( id INT, name VARCHAR(5) ) PARTITION BY HASH(id) PARTITIONS 2; 假如Linux 上的MySQL 的一个RPM安装,这个语句在目录/var/lib/mysql/test下创建了两个.MYD文件,这两个 文件可以在bash shell中查看,结果如下: /var/lib/mysql/test> ls *.MYD -l -rw-rw---- 1 mysql mysql 0 2005-11-04 18:41 tnhash_p0.MYD -rw-rw---- 1 mysql mysql 0 2005-11-04 18:41 tnhash_p1.MYD 注意:每个文件的大小为0字节。现在在表tnhash 中插入一行id列值为NULL的行,然后验证该行已经被插入: mysql> INSERT INTO tnhash VALUES (NULL, 'sam'); Query OK, 1 row affected (0.00 sec) mysql> SELECT * FROM tnhash; +------+------+ | id | name | +------+------+ | NULL | sam | +------+------+ 1 row in set (0.01 sec) 回想一下,对于任意的整数N,NULL MOD N 的值总是等于NULL。这个结果在确定正确的分区方面被认为是0。 回到系统shell(仍然假定bash用于这个目的) ,通过再次列出数据文件,可以看出值被成功地插入到第一个分 区(默认名称为p0)中: var/lib/mysql/test> ls *.MYD -l -rw-rw---- 1 mysql mysql 20 2005-11-04 18:44 tnhash_p0.MYD -rw-rw---- 1 mysql mysql 0 2005-11-04 18:41 tnhash_p1.MYD 可以看出INSERT语句只修改了文件tnhash_p0.MYD,它在磁盘上的尺寸增加了,而没有影响其他的文件。 假定有下面的一个表: CREATE TABLE tndate ( id INT, dt DATE ) PARTITION BY RANGE( YEAR(dt) ) ( PARTITION p0 VALUES LESS THAN (1990), PARTITION p1 VALUES LESS THAN (2000), PARTITION p2 VALUES LESS THAN MAXVALUE ); 像其他的MySQL函数一样,YEAR(NULL)返回NULL值。一个dt列值为NULL的行,其分区表达式的计算结果被视 为0,该行被插入到分区p0中。 分区管理 18.3.1. RANGE和LIST分区的管理 18.3.2. HASH和KEY分区的管理 18.3.3. 分区维护 18.3.4. 获取关于分区的信息 MySQL 5.1 提供了许多修改分区表的方式。添加、删除、重新定义、合并或拆分已经存在的分区是可能的。所 有这些操作都可以通过使用ALTER TABLE 命令的分区扩展来实现(关于语法的定义,请参见13.1.2节,“ALTER TABLE语法” )。也有获得分区表和分区信息的方式。在本节,我们讨论下面这些主题: · 按RANGE或LIST分区的表的分区管理的有关信息,请参见18.3.1节,“RANGE和LIST分区的管理”。 · 关于HASH和KEY分区管理的讨论,请参见18.3.2节,“HASH和KEY分区的管理”。 · MySQL 5.1中提供的、获得关于分区表和分区信息的机制的讨论,请参见18.3.4节,“获取关于分区的信 息” 。 · 关于执行分区维护操作的讨论,请参见18.3.3节,“分区维护”。 注释:在MySQL 5.1中,一个分区表的所有分区都必须有子分区同样的名字,并且一旦表已经创建,再改变子 分区是不可能的。 要点:当前,从5.1系列起建立的MySQL 服务器就把“ALTER TABLE ... PARTITION BY ...”作为有效的语法,但 是这个语句目前还不起作用。我们期望MySQL 5.1达到生产状态时,能够按照下面的描述实现该语句的功能。 要改变一个表的分区模式,只需要使用带有一个“partition_options”子句的ALTER TABLE 的命令。这个子句和 与创建一个分区表的CREATE TABLE命令一同使用的子句有相同的语法,并且总是以关键字PARTITION BY 开 头。例如,假设有一个使用下面CREATE TABLE语句建立的按照RANGE分区的表: CREATE TABLE trb3 (id INT, name VARCHAR(50), purchased DATE) PARTITION BY RANGE(YEAR(purchased)) ( PARTITION p0 VALUES LESS THAN (1990), PARTITION p1 VALUES LESS THAN (1995), PARTITION p2 VALUES LESS THAN (2000), PARTITION p3 VALUES LESS THAN (2005) ); 现在,要把这个表按照使用id列值作为键的基础,通过KEY分区把它重新分成两个分区,可以使用下面的语 句: ALTER TABLE trb3 PARTITION BY KEY(id) PARTITIONS 2; 这和先删除这个表、然后使用“CREATE TABLE trb3 PARTITION BY KEY(id) PARTITIONS 2;”重新创建这个表 具有同样的效果。 18.3.1. RANGE和LIST分区的管理 关于如何添加和删除分区的处理,RANGE和LIST分区非常相似。基于这个原因,我们在本节讨论这两种分区的 管理。关于HASH和KEY分区管理的信息,请参见18.3.2节,“HASH和KEY分区的管理”。删除一 个RANGE或LIST分区比增加一个分区要更加简单易懂,所以我们先讨论前者。 从一个按照RANGE或LIST分区的表中删除一个分区,可以使用带一个DROP PARTITION子句的ALTER TABLE命 令来实现。这里有一个非常基本的例子,假设已经使用下面的CREATE TABLE和INSERT语句创建了一个按 照RANGE分区的表,并且已经插入了10条记录: mysql> CREATE TABLE tr (id INT, name VARCHAR(50), purchased DATE) -> PARTITION BY RANGE(YEAR(purchased)) -> ( -> PARTITION p0 VALUES LESS THAN (1990), -> PARTITION p1 VALUES LESS THAN (1995), -> PARTITION p2 VALUES LESS THAN (2000), -> PARTITION p3 VALUES LESS THAN (2005) -> ); Query OK, 0 rows affected (0.01 sec) mysql> INSERT INTO tr VALUES -> (1, 'desk organiser', '2003-10-15'), -> (2, 'CD player', '1993-11-05'), -> (3, 'TV set', '1996-03-10'), -> (4, 'bookcase', '1982-01-10'), -> (5, 'exercise bike', '2004-05-09'), -> (6, 'sofa', '1987-06-05'), -> (7, 'popcorn maker', '2001-11-22'), -> (8, 'aquarium', '1992-08-04'), -> (9, 'study desk', '1984-09-16'), -> (10, 'lava lamp', '1998-12-25'); Query OK, 10 rows affected (0.01 sec) 可以通过使用下面的命令查看那些记录已经插入到了分区p2中: mysql> SELECT * FROM tr -> WHERE purchased BETWEEN '1995-01-01' AND '1999-12-31'; +------+-----------+------------+ | id | name | purchased | +------+-----------+------------+ | 3 | TV set | 1996-03-10 | | 10 | lava lamp | 1998-12-25 | +------+-----------+------------+ 2 rows in set (0.00 sec) 要删除名字为p2的分区,执行下面的命令: mysql> ALTER TABLE tr DROP PARTITION p2; Query OK, 0 rows affected (0.03 sec) 记住下面一点非常重要:当删除了一个分区,也同时删除了该分区中所有的数据。可以通过重新运行前面 的SELECT查询来验证这一点: mysql> SELECT * FROM tr WHERE purchased -> BETWEEN '1995-01-01' AND '1999-12-31'; Empty set (0.00 sec) 如果希望从所有分区删除所有的数据,但是又保留表的定义和表的分区模式,使用TRUNCATE TABLE命 令。(请参见13.2.9节,“TRUNCATE语法”)。 如果希望改变表的分区而又不丢失数据,使用“ALTER TABLE ... REORGANIZE PARTITION”语句。参见下面的 内容,或者在13.1.2节,“ALTER TABLE语法” 中参考关于REORGANIZE PARTITION的信息。 如果现在执行一个SHOW CREATE TABLE命令,可以观察到表的分区结构是如何被改变的: mysql> SHOW CREATE TABLE tr\G *************************** 1. row *************************** Table: tr Create Table: CREATE TABLE `tr` ( `id` int(11) default NULL, `name` varchar(50) default NULL, `purchased` date default NULL ) ENGINE=MyISAM DEFAULT CHARSET=latin1 PARTITION BY RANGE (YEAR(purchased)) ( PARTITION p0 VALUES LESS THAN (1990) ENGINE = MyISAM, PARTITION p1 VALUES LESS THAN (1995) ENGINE = MyISAM, PARTITION p3 VALUES LESS THAN (2005) ENGINE = MyISAM ) 1 row in set (0.01 sec) 如果插入购买日期列的值在'1995-01-01'和'2004-12-31'之间(含)的新行到已经修改后的表中时,这些行将 被保存在分区p3中。可以通过下面的方式来验证这一点: mysql> INSERT INTO tr VALUES (11, 'pencil holder', '1995-07-12'); Query OK, 1 row affected (0.00 sec) mysql> SELECT * FROM tr WHERE purchased -> BETWEEN '1995-01-01' AND '2004-12-31'; +------+----------------+------------+ | id | name | purchased | +------+----------------+------------+ | 11 | pencil holder | 1995-07-12 | | 1 | desk organiser | 2003-10-15 | | 5 | exercise bike | 2004-05-09 | | 7 | popcorn maker | 2001-11-22 | +------+----------------+------------+ 4 rows in set (0.00 sec) mysql> ALTER TABLE tr DROP PARTITION p3; Query OK, 0 rows affected (0.03 sec) mysql> SELECT * FROM tr WHERE purchased -> BETWEEN '1995-01-01' AND '2004-12-31'; Empty set (0.00 sec) 注意:由“ALTER TABLE ... DROP PARTITION”语句引起的、从表中删除的行数并没有被服务器报告出来,就 好像通过同等的DELETE查询操作一样。 删除LIST分区使用和删除RANGE分区完全相同的“ALTER TABLE ... DROP PARTITION”语法。但是,在对其后 使用这个表的影响方面,还是有重大的区别:在这个表中,再也不能插入这么一些行,这些行的列值包含在定 义已经删除了的分区的值列表中 (有关示例,请参见18.2.2节,“LIST分区” )。 要增加一个新的RANGE或LIST分区到一个前面已经分区了的表,使用“ALTER TABLE ... ADD PARTITION”语 句。对于使用RANGE分区的表,可以用这个语句添加新的区间到已有分区的序列的前面或后面。例如,假设有 一个包含你所在组织的全体成员数据的分区表,该表的定义如下: CREATE TABLE members ( id INT, fname VARCHAR(25), lname VARCHAR(25), dob DATE ) PARTITION BY RANGE(YEAR(dob)) ( PARTITION p0 VALUES LESS THAN (1970), PARTITION p1 VALUES LESS THAN (1980), PARTITION p2 VALUES LESS THAN (1990) ); 进一步假设成员的最小年纪是16岁。随着日历接近2005年年底,你会认识到不久将要接纳1990年(以及以后 年份)出生的成员。可以按照下面的方式,修改成员表来容纳出生在1990-1999年之间的成员: ALTER TABLE ADD PARTITION (PARTITION p3 VALUES LESS THAN (2000)); 要点:对于通过RANGE分区的表,只可以使用ADD PARTITION添加新的分区到分区列表的高端。设法通过这 种方式在现有分区的前面或之间增加一个新的分区,将会导致下面的一个错误: mysql> ALTER TABLE members ADD PARTITION (PARTITION p3 VALUES LESS THAN (1960)); 错误1463 (HY000): 对每个分区,VALUES LESS THAN 值必须严格增长 采用一个类似的方式,可以增加新的分区到已经通过LIST分区的表。例如,假定有如下定义的一个表: CREATE TABLE tt ( id INT, data INT ) PARTITION BY LIST(data) ( PARTITION p0 VALUES IN (5, 10, 15), PARTITION p1 VALUES IN (6, 12, 18) ); 可以通过下面的方法添加一个新的分区,用来保存拥有数据列值7,14和21的行: ALTER TABLE tt ADD PARTITION (PARTITION p2 VALUES IN (7, 14, 21)); 注意:不能添加这样一个新的LIST分区,该分区包含有已经包含在现有分区值列表中的任意值。如果试图这样 做,将会导致错误: mysql> ALTER TABLE tt ADD PARTITION (PARTITION np VALUES IN (4, 8, 12)); 错误1465 (HY000): 在LIST分区中,同一个常数的多次定义 因为带有数据列值12的任何行都已经分配给了分区p1,所以不能在表tt上再创建一个其值列表包括12的新分 区。为了实现这一点,可以先删除分区p1,添加分区np,然后使用修正后的定义添加一个新的分区p1。但 是,正如我们前面讨论过的,这将导致保存在分区p1中的所有数据丢失——而这往往并不是你所真正想要做 的。另外一种解决方法可能是,建立一个带有新分区的表的副本,然后使用“CREATE TABLE ... SELECT ...”把 数据拷贝到该新表中,然后删除旧表,重新命名新表,但是,当需要处理大量的数据时,这可能是非常耗时 的。在需要高可用性的场合,这也可能是不可行的。 幸运地是,MySQL 的分区实现提供了在不丢失数据的条件下重新定义分区的方式。让我们首先看两个涉及 到RANGE分区的简单例子。回想一下现在定义如下的成员表: mysql> SHOW CREATE TABLE members\G *************************** 1. row *************************** Table: members Create Table: CREATE TABLE `members` ( `id` int(11) default NULL, `fname` varchar(25) default NULL, `lname` varchar(25) default NULL, `dob` date default NULL ) ENGINE=MyISAM DEFAULT CHARSET=latin1 PARTITION BY RANGE (YEAR(dob)) ( PARTITION p0 VALUES LESS THAN (1970) ENGINE = MyISAM, PARTITION p1 VALUES LESS THAN (1980) ENGINE = MyISAM, PARTITION p2 VALUES LESS THAN (1990) ENGINE = MyISAM. PARTITION p3 VALUES LESS THAN (2000) ENGINE = MyISAM ) 假定想要把表示出生在1960年前成员的所有行移入到一个分开的分区中。正如我们前面看到的,不能通过使 用“ALTER TABLE ... ADD PARTITION”来实现这一点。但是,要实现这一点,可以使用ALTER TABLE上的另外 一个与分区有关的扩展,具体实现如下: ALTER TABLE members REORGANIZE PARTITION p0 INTO ( PARTITION s0 VALUES LESS THAN (1960), PARTITION s1 VALUES LESS THAN (1970) ); 实际上,这个命令把分区p0分成了两个新的分区s0和s1。同时,它还根据包含在两个“PARTITION ... VALUES ...”子句中的规则,把保存在分区p0中的数据移入到两个新的分区中,所以分区s0中只包含YEAR(dob)小 于1960的那些行,s1中包含那些YEAR(dob)大于或等于1960但是小于1970的行。 一个REORGANIZE PARTITION语句也可以用来合并相邻的分区。可以使用如下的语句恢复成员表到它以前的分 区: ALTER TABLE members REORGANIZE PARTITION s0,s1 INTO ( PARTITION p0 VALUES LESS THAN (1970) ); 使用“REORGANIZE PARTITION”拆分或合并分区,没有数据丢失。在执行上面的语句中,MySQL 把保存在分 区s0和s1中的所有数据都移到分区p0中。 “REORGANIZE PARTITION”的基本语法是: ALTER TABLE tbl_name REORGANIZE PARTITION partition_list INTO (partition_definitions); 其中,tbl_name 是分区表的名称,partition_list 是通过逗号分开的、一个或多个将要被改变的现有分区的列 表。partition_definitions 是一个是通过逗号分开的、新分区定义的列表,它遵循与用在“CREATE TABLE”中 的partition_definitions 相同的规则 (请参见13.1.5节,“CREATE TABLE语法”)。应当注意到,在把多少个分区 合并到一个分区或把一个分区拆分成多少个分区方面,没有限制。例如,可以重新组织成员表的四个分区成两 个分区,具体实现如下: ALTER TABLE members REORGANIZE PARTITION p0,p1,p2,p3 INTO ( PARTITION m0 VALUES LESS THAN (1980), PARTITION m1 VALUES LESS THAN (2000) ); LIST REORGANIZE PARTITION 同样,对于按分区的表,也可以使用。让我们回到那个问题,即增加一个新的 分区到已经按照LIST分区的表tt中,但是因为该新分区有一个值已经存在于现有分区的值列表中,添加新的分 区失败。我们可以通过先添加只包含非冲突值的分区,然后重新组织该新分区和现有的那个分区,以便保存在 现有的那个分区中的值现在移到了新的分区中,来处理这个问题: ALTER TABLE tt ADD PARTITION (PARTITION np VALUES IN (4, 8)); ALTER TABLE tt REORGANIZE PARTITION p1,np INTO ( PARTITION p1 VALUES IN (6, 18), PARTITION np VALUES in (4, 8, 12) ); 当使用“ALTER TABLE ... REORGANIZE PARTITION”来对已经按照RANGE和LIST分区表进行重新分区时,下面 是一些要记住的关键点: · 用来确定新分区模式的PARTITION子句使用与用在CREATE TABLE中确定分区模式的PARTITION子句相 同的规则。 最重要的是,应该记住:新分区模式不能有任何重叠的区间(适用于按照RANGE分区的表)或值集合(适用于 重新组织按照LIST分区的表)。 · partition_definitions 列表中分区的合集应该与在partition_list 中命名分区的合集占有相同的区间或值集 合。 例如,在本节中用作例子的成员表中,分区p1和p2总共覆盖了1980到1999的这些年。因此,对这两个分区的 重新组织都应该覆盖相同范围的年份。 · 对于按照RANGE分区的表,只能重新组织相邻的分区;不能跳过RANGE分区。 例如,不能使用以“ALTER TABLE members REORGANIZE PARTITION p0,p2 INTO ...”开头的语句,来重新组 织本节中用作例子的成员表。因为,p0覆盖了1970年以前的年份,而p2覆盖了从1990到1999(包 括1990和1999)之间的年份,因而这两个分区不是相邻的分区。 · 不能使用REORGANIZE PARTITION来改变表的分区类型;也就是说,例如,不能把RANGE分区变 为HASH分区,反之亦然。也不能使用该命令来改变分区表达式或列。如果想在不删除和重建表的条件下实现 这两个任务,可以使用“ALTER TABLE ... PARTITION BY ....”,例如: · ALTER TABLE members · PARTITION BY HASH(YEAR(dob)) · PARTITIONS 8; 注释:在MySQL 5.1发布前的版本中,“ALTER TABLE ... PARTITION BY ...”还没有实现。作为替代,要么使用 先删除表,然后使用想要的分区重建表,或者——如果需要保留已经存储在表中的数据——可以使用“CREATE TABLE ... SELECT ...”来创建新的表,然后从旧表中把数据拷贝到新表中,再删除旧表,如有必要,最后重新 命名新表。 分区维护 注释:实际上,本节讨论的命令还没有在MySQL 5.1中实现, 在这里提出的目的,是为了在5.1版投产前的开 发周期期间,引出来自用户测试该软件的反馈意见。(换句话说,就是“请不要反馈这样的缺陷,说这些命令不 起作用”)。随着MySQL5.1版开发的继续,这些信息很有可能发生变化。随着分区功能的实现和提高,我们将 更新本节的内容。 MySQL 5.1中可以执行许多分区维护的任务。对于分区表,MySQL不支持命令CHECK TABLE,OPTIMIZE TABLE,ANALYZE TABLE,或REPAIR TABLE。作为替代,可以使用ALTER TABLE 的许多扩展来在一个或多个 分区上直接地执行这些操作,如下面列出的那样: · 重建分区: 这和先删除保存在分区中的所有记录,然后重新插入它们,具有同样的效果。它可用于整理 分区碎片。 示例: ALTER TABLE t1 REBUILD PARTITION (p0, p1); · 优化分区:如果从分区中删除了大量的行,或者对一个带有可变长度的行(也就是说, 有VARCHAR,BLOB,或TEXT类型的列)作了许多修改,可以使用“ALTER TABLE ... OPTIMIZE PARTITION”来收回没有使用的空间,并整理分区数据文件的碎片。 示例: ALTER TABLE t1 OPTIMIZE PARTITION (p0, p1); 在一个给定的分区表上使用“OPTIMIZE PARTITION”等同于在那个分区上运行CHECK PARTITION,ANALYZE PARTITION,和REPAIR PARTITION。 · 分析分区:读取并保存分区的键分布。 示例: ALTER TABLE t1 ANALYZE PARTITION (p3); · 修补分区: 修补被破坏的分区。 示例: ALTER TABLE t1 REPAIR PARTITION (p0,p1); · 检查分区: 可以使用几乎与对非分区表使用CHECK TABLE 相同的方式检查分区。 示例: ALTER TABLE trb3 CHECK PARTITION (p1); 这个命令可以告诉你表t1的分区p1中的数据或索引是否已经被破坏。如果发生了这种情况,使用“ALTER TABLE ... REPAIR PARTITION”来修补该分区。 还可以使用mysqlcheck或myisamchk 应用程序,在对表进行分区时所产生的、单独的MYI文件上进行操 作,来完成这些任务。请参见8.7节,“mysqlcheck:表维护和维修程序”。(在pre-alpha编码中,这个功能已 经可以使用)。 18.3.4. 获取关于分区的信息 本节讨论获取关于现有分区的信息。这个功能仍然处于计划阶段,所以现阶段在这里描述的,实际上是我们想 要在MySQL 5.1中实现的一个概观。 如在本章中别处讨论的一样,在SHOW CREATE TABLE的输出中包含了用于创建分区表的PARTITION BY子 句。例如: mysql> SHOW CREATE TABLE trb3\G *************************** 1. row *************************** Table: trb3 Create Table: CREATE TABLE `trb3` ( `id` int(11) default NULL, `name` varchar(50) default NULL, `purchased` date default NULL ) ENGINE=MyISAM DEFAULT CHARSET=latin1 PARTITION BY RANGE (YEAR(purchased)) ( PARTITION p0 VALUES LESS THAN (1990) ENGINE = MyISAM, PARTITION p1 VALUES LESS THAN (1995) ENGINE = MyISAM, PARTITION p2 VALUES LESS THAN (2000) ENGINE = MyISAM, PARTITION p3 VALUES LESS THAN (2005) ENGINE = MyISAM ) 1 row in set (0.00 sec) 注释:当前,对于按HASH或KEY分区的表,PARTITIONS子句并不显示。 (Bug #14327) SHOW TABLE STATUS用于分区表,它的输出与用于非分区表的输出相同,除了引擎(Engine)列总是包 含'PARTITION'值。(关于这个命令的更多信息,参见13.5.4.18节,“SHOW TABLE STATUS语法”)。要获取单 个分区的状态信息,我们计划实现一个SHOW PARTITION STATUS命令(请参见下面)。 计划用于分区表的、两个附加的SHOW命令是: · SHOW PARTITIONS 这个命令预期其功能类似于SHOW TABLES和SHOW DATABASES,除了该命令将列出的是分区而不是表或数据 库。这个命令的输出可能包含单个称为Partitions_in_tbl_name 的列,其中tbl_name 是分区表的名字。对 于SHOW TABLES命令而言,如果一旦选择了一个数据库,随后该数据库将作为SHOW TABLES命令的默认数据 库。但是由于SHOW PARTITIONS命令不可能用这样的方式来“选择”一个表,它很可能需要使用FROM子句,以 便MySQL知道要显示的是哪个表的分区信息。 · SHOW PARTITION STATUS 这个命令将提供关于一个或多个分区的详细状态信息。它的输出很可能包含有与SHOW TABLE STATUS 的输出 相同或类似的列,此外,还包括显示用于分区的数据和索引路径的附加列。这个命令可能支持LIKE和FROM子 句,这样使得通过名字获得关于一个给定分区的信息,或者获得关于属于指定表或数据库的分区的信息,成为 可能。 扩展INFORMATION_SCHEMA 数据库的计划也在进行中,以便提供关于分区表和分区的信息。这个计划当前还 处一个在非常早的阶段;随着补充的信息变得可用,以及任何新的、与分区有关的INFORMATION_SCHEMA扩 展得以实现,我们将更新手册相关部分的内容。 这是MySQL参考手册的翻译版本,关于MySQL参考手册,请访问dev.mysql.com。原始参考手册为英文版,与 英文版参考手册相比,本翻译版可能不是最新 存储程序和函数 目录 20.1. 存储程序和授权表 20.2. 存储程序的语法 20.2.1. CREATE PROCEDURE和CREATE FUNCTION 20.2.2. ALTER PROCEDURE和ALTER FUNCTION 20.2.3. DROP PROCEDURE和和DROP FUNCTION 20.2.4. SHOW CREATE PROCEDURE和SHOW CREATE FUNCTION 20.2.5. SHOW PROCEDURE STATUS和SHOW FUNCTION STATUS 20.2.6. CALL语句 20.2.7. BEGIN ... END复合语句 20.2.8. DECLARE语句 20.2.9. 存储程序中的变量 20.2.10. 条件和处理程序 20.2.11. 光标 20.2.12. 流程控制构造 20.3. 存储程序、函数、触发程序和复制:常见问题 20.4. 存储子程序和触发程序的二进制日志功能 MySQL 5.1版支持存储程序和函数。一个存储程序是可以被存储在服务器中的一套SQL语句。一旦它被存储 了,客户端不需要再重新发布单独的语句,而是可以引用存储程序来替代。 下面一些情况下存储程序尤其有用: · 当用不同语言编写多客户应用程序,或多客户应用程序在不同平台上运行且需要执行相同的数据库操作 之时。 · 安全极为重要之时。比如,银行对所有普通操作使用存储程序。这提供一个坚固而安全的环境,程序可 以确保每一个操作都被妥善记入日志。在这样一个设置中,应用程序和用户不可能直接访问数据库表,但是仅 可以执行指定的存储程序。 存储程序可以提供改良后的性能,因为只有较少的信息需要在服务器和客户算之间传送。代价是增加数据库服 务器系统的负荷,因为更多的工作在服务器这边完成,更少的在客户端(应用程序)那边完成上。如果许多客 户端机器(比如网页服务器)只由一个或少数几个数据库服务器提供服务,可以考虑一下存储程序。 存储程序也允许你在数据库服务器上有函数库。这是一个被现代应用程序语言共享的特征,它允许这样的内部 设计,比如通过使用类。使用这些客户端应用程序语言特征对甚至于数据库使用范围以外的编程人员都有好 处。 MySQL为存储程序遵循SQL:2003语法,这个语法也被用在IBM的DB2数据库上。 MySQL对存储程序的实现还在进度中。所有本章叙述的语法都被支持,在有限制或扩展的地方会恰当地指出 来。有关使用存储程序的限制的更多讨论在附录I, 特性限制里提到。 如20.4节,“存储子程序和触发程序的二进制日志功能”里所说的, 存储子程序的二进制日志功能已经完成。 20.1. 存储程序和授权表 存储程序需要在mysql数据库中有proc表。这个表在MySQL 5.1安装过程中创建。如果你从早期的版本升级 到MySQL 5.1 ,请确定更新你的授权表以确保proc表的存在。请参阅2.10.2节“升级授权表”。 在MySQL 5.1中,授权系统如下考虑存储子程序: · 创建存储子程序需要CREATE ROUTINE权限。 · 提醒或移除存储子程序需要ALTER ROUTINE权限。这个权限自动授予子程序的创建者。 · 执行子程序需要EXECUTE权限。然而,这个权限自动授予子程序的创建者。同样,子程序默认的SQL SECURITY 特征是DEFINER,它允许用该子程序访问数据库的用户与执行子程序联系到一起。 20.2. 存储程序的语法 20.2.1. CREATE PROCEDURE和CREATE FUNCTION 20.2.2. ALTER PROCEDURE和ALTER FUNCTION 20.2.3. DROP PROCEDURE和DROP FUNCTION 20.2.4. SHOW CREATE PROCEDURE和SHOW CREATE FUNCTION 20.2.5. SHOW PROCEDURE STATUS和SHOW FUNCTION STATUS 20.2.6. CALL语句 20.2.7. BEGIN ... END复合语句 20.2.8. DECLARE语句 20.2.9. 存储程序中的变量 20.2.10. 条件和处理程序 20.2.11. 光标 20.2.12. 流程控制构造 存储程序和函数是用CREATE PROCEDURE和CREATE FUNCTION语句创建的子程序。一个子程序要么是一个程 序要么是一个函数。使用CALL语句来调用程序,程序只能用输出变量传回值。就像别其它函数调用一样,函 数可以被从语句外调用(即通过引用函数名),函数能返回标量值。存储子程序也可以调用其它存储子程序。 在MySQL 5.1中,一个存储子程序或函数与特定的数据库相联系。这里有几个意思: · 当一个子程序被调用时,一个隐含的USE db_name 被执行(当子程序终止时停止执行)。存储子程序 内的USE语句时不允许的。 · 你可以使用数据库名限定子程序名。这可以被用来引用一个不在当前数据库中的子程序。比如,要引用 一个与test数据库关联的存储程序p或函数f,你可以说CALL test.p()或test.f()。 · 数据库移除的时候,与它关联的所有存储子程序也都被移除。 MySQL 支持非常有用的扩展,即它允许在存储程序中使用常规的SELECT语句(那就是说,不使用光标或局部 变量)。这个一个查询的结果包被简单地直接送到客户端。多SELECT语句生成多个结果包,所以客户端必须 使用支持多结果包的MySQL客户端库。这意味这客户端必须使用至少MySQL 4.1以来的近期版本上的客户端 库。 下面一节描述用来创建,改变,移除和查询存储程序和函数的语法。 20.2.1. CREATE PROCEDURE和CREATE FUNCTION CREATE PROCEDURE sp_name ([proc_parameter[,...]]) [characteristic ...] routine_body CREATE FUNCTION sp_name ([func_parameter[,...]]) RETURNS type [characteristic ...] routine_body proc_parameter: [ IN | OUT | INOUT ] param_name type func_parameter: param_name type type: Any valid MySQL data type characteristic: LANGUAGE SQL | [NOT] DETERMINISTIC | { CONTAINS SQL | NO SQL | READS SQL DATA | MODIFIES SQL DATA } | SQL SECURITY { DEFINER | INVOKER } | COMMENT 'string' routine_body: Valid SQL procedure statement or statements 这些语句创建存储子程序。要在MySQL 5.1中创建子程序,必须具有CREATE ROUTINE权限,并且ALTER ROUTINE和EXECUTE权限被自动授予它的创建者。如果二进制日志功能被允许,你也可能需要SUPER权限,请 参阅20.4节,“存储子程序和触发程序的二进制日志功能”。 默认地,子程序与当前数据库关联。要明确地把子程序与一个给定数据库关联起来,可以在创建子程序的时候 指定其名字为db_name.sp_name。 如果子程序名和内建的SQL函数名一样,定义子程序时,你需要在这个名字和随后括号中间插入一个空格,否 则发生语法错误。当你随后调用子程序的时候也要插入。为此,即使有可能出现这种情况,我们还是建议最好 避免给你自己的存储子程序取与存在的SQL函数一样的名字。 由括号包围的参数列必须总是存在。如果没有参数,也该使用一个空参数列()。每个参数默认都是一个IN参 数。要指定为其它参数,可在参数名之前使用关键词 OUT或INOUT 注意: 指定参数为IN, OUT, 或INOUT 只对PROCEDURE是合法的。(FUNCTION参数总是被认为是IN参数) RETURNS字句只能对FUNCTION做指定,对函数而言这是强制的。它用来指定函数的返回类型,而且函数体必 须包含一个RETURN value语句。 routine_body 包含合法的SQL过程语句。可以使用复合语句语法,请参阅20.2.7节,“BEGIN ... END复合语 句”。复合语句可以包含声明,循环和其它控制结构语句。这些语句的语法在本章后免介绍,举例,请参 阅20.2.8节,“DECLARE语句”和20.2.12节,“流程控制构造”。 CREATE FUNCTION语句被用在更早的MySQL版本上以支持UDF (自定义函数)。请参 阅27.2节,“给MySQL添加新函数”。 UDF继续被支持,即使现在有了存储函数。UDF会被认为一个外部存储函 数。然而,不要让存储函数与UDF函数共享名字空间。 外部存储程序的框架将在不久的将来引入。这将允许你用SQL之外的语言编写存储程序。最可能的是,第一个 被支持语言是PHP,因为核心PHP引擎很小,线程安全,且可以被方便地嵌入。因为框架是公开的,它希望许 多其它语言也能被支持。 如果程序或线程总是对同样的输入参数产生同样的结果,则被认为它是“确定的”,否则就是“非确定”的。如果 既没有给定DETERMINISTIC也没有给定NOT DETERMINISTIC,默认的就是NOT DETERMINISTIC。 为进行复制,使用NOW()函数(或它的同义词)或RAND()函数会不必要地使得一个子程序非确定。 对NOW()而言,二进制日志包括时间戳并被正确复制。RAND() 只要在一个子程序被内应用一次也会被正确复 制。(你可以把子程序执行时间戳和随机数种子认为强制输入,它们在主从上是同样的。) 当前来讲,DETERMINISTIC特征被接受,但还没有被优化程序所使用。然而如果二进制日志功能被允许了,这 个特征影响到MySQL是否会接受子程序定义。请参阅20.4节,“存储子程序和触发程序的二进制日志功能”。 一些特征提供子程序使用数据的内在信息。CONTAINS SQL表示子程序不包含读或写数据的语句。NO SQL表 示子程序不包含SQL语句。READS SQL DATA表示子程序包含读数据的语句,但不包含写数据的语 句。MODIFIES SQL DATA表示子程序包含写数据的语句。如果这些特征没有明确给定,默认的是CONTAINS SQL。 SQL SECURITY特征可以用来指定子程序该用创建子程序者的许可来执行,还是使用调用者的许可来执行。默 认值是DEFINER。在SQL:2003中者是一个新特性。创建者或调用者必须由访问子程序关联的数据库的许可。 在MySQL 5.1中,必须有EXECUTE权限才能执行子程序。必须拥有这个权限的用户要么是定义者,要么是调用 者,这取决于SQL SECURITY特征是如何设置的。 MySQL存储sql_mode系统变量设置,这个设置在子程序被创建的时候起作用,MySQL总是强制使用这个设置 来执行子程序。 COMMENT子句是一个MySQL的扩展,它可以被用来描述存储程序。这个信息被SHOW CREATE PROCEDURE和 SHOW CREATE FUNCTION语句来显示。 MySQL允许子程序包含DDL语句,如CREATE和DROP。MySQL也允许存储程序(但不是存储函数)包含SQL 交互语句,如COMMIT。存储函数不可以包含那些做明确的和绝对的提交或者做回滚的语。SQL标准不要求对 这些语句的支持,SQL标准声明每个DBMS提供商可以决定是否允许支持这些语句。 存储子程序不能使用LOAD DATA INFILE。 返回结果包的语句不能被用在存储函数种。这包括不使用INTO给变量读取列值的SELECT语句,SHOW 语句, 及其它诸如EXPLAIN这样的语句。对于可在函数定义时间被决定要返回一个结果包的语句,发生一个允许从函 数错误返回结果包的Not(ER_SP_NO_RETSET_IN_FUNC)。对于只可在运行时决定要返回一个结果包的语 句, 发生一个不能在给定上下文错误返回结果包的PROCEDURE %s (ER_SP_BADSELECT)。 下面是一个使用OUT参数的简单的存储程序的例子。例子为,在程序被定义的时候,用mysql客户 端delimiter命令来把语句定界符从 ;变为//。这就允许用在程序体中的;定界符被传递到服务器而不是 被mysql自己来解释。 mysql> delimiter // mysql> CREATE PROCEDURE simpleproc (OUT param1 INT) -> BEGIN -> SELECT COUNT(*) INTO param1 FROM t; -> END -> // Query OK, 0 rows affected (0.00 sec) mysql> delimiter ; mysql> CALL simpleproc(@a); Query OK, 0 rows affected (0.00 sec) mysql> SELECT @a; +------+ | @a | +------+ | 3 | +------+ 1 row in set (0.00 sec) 当使用delimiter命令时,你应该避免使用反斜杠(‘\’)字符,因为那是MySQL的转义字符。 下列是一个例子,一个采用参数的函数使用一个SQL函数执行一个操作,并返回结果: mysql> delimiter // mysql> CREATE FUNCTION hello (s CHAR(20)) RETURNS CHAR(50) -> RETURN CONCAT('Hello, ',s,'!'); -> // Query OK, 0 rows affected (0.00 sec) mysql> delimiter ; mysql> SELECT hello('world'); +----------------+ | hello('world') | +----------------+ | Hello, world! | +----------------+ 1 row in set (0.00 sec) 如果在存储函数中的RETURN语句返回一个类型不同于在函数的RETURNS子句中指定类型的值,返回值被强制 为恰当的类型。比如,如果一个函数返回一个ENUM或SET值,但是RETURN语句返回一个整数,对于SET成员 集的相应的ENUM成员,从函数返回的值是字符串。 20.2.2. ALTER PROCEDURE和ALTER FUNCTION ALTER {PROCEDURE | FUNCTION} sp_name [characteristic ...] characteristic: { CONTAINS SQL | NO SQL | READS SQL DATA | MODIFIES SQL DATA } | SQL SECURITY { DEFINER | INVOKER } | COMMENT 'string' 这个语句可以被用来改变一个存储程序或函数的特征。在MySQL 5.1中,你必须用ALTER ROUTINE权限才可用 此子程序。这个权限被自动授予子程序的创建者。如20.4节,“存储子程序和触发程序的二进制日志功能”中所 述, 如果二进制日志功能被允许了,你可能也需要SUPER权限。 在ALTER PROCEDURE和ALTER FUNCTION语句中,可以指定超过一个的改变。 20.2.3. DROP PROCEDURE和DROP FUNCTION DROP {PROCEDURE | FUNCTION} [IF EXISTS] sp_name 这个语句被用来移除一个存储程序或函数。即,从服务器移除一个制定的子程序。在MySQL 5.1中,你必须 有ALTER ROUTINE权限才可用此子程序。这个权限被自动授予子程序的创建者。 IF EXISTS 子句是一个MySQL的扩展。如果程序或函数不存储,它防止发生错误。产生一个可以用SHOW WARNINGS查看的警告。 20.2.4. SHOW CREATE PROCEDURE和SHOW CREATE FUNCTION SHOW CREATE {PROCEDURE | FUNCTION} sp_name 这个语句是一个MySQL的扩展。类似于SHOW CREATE TABLE,它返回一个可用来重新创建已命名子程序的确 切字符串。 mysql> SHOW CREATE FUNCTION test.hello\G *************************** 1. row *************************** Function: hello sql_mode: Create Function: CREATE FUNCTION `test`.`hello`(s CHAR(20)) RETURNS CHAR(50) RETURN CONCAT('Hello, ',s,'!') 20.2.5. SHOW PROCEDURE STATUS和SHOW FUNCTION STATUS SHOW {PROCEDURE | FUNCTION} STATUS [LIKE 'pattern'] 这个语句是一个MySQL的扩展。它返回子程序的特征,如数据库,名字,类型,创建者及创建和修改日期。如 果没有指定样式,根据你使用的语句,所有存储程序和所有存储函数的信息都被列出。 mysql> SHOW FUNCTION STATUS LIKE 'hello'\G *************************** 1. row *************************** Db: test Name: hello Type: FUNCTION Definer: testuser@localhost Modified: 2004-08-03 15:29:37 Created: 2004-08-03 15:29:37 Security_type: DEFINER Comment: 你可以从INFORMATION_SCHEMA中的ROUTINES表获得有关存储子程序的信息。请参 阅23.1.14节,“INFORMATION_SCHEMA ROUTINES 表”。 20.2.6. CALL语句 CALL sp_name([parameter[,...]]) CALL语句调用一个先前用CREATE PROCEDURE创建的程序。 CALL语句可以用声明为OUT或的INOUT参数的参数给它的调用者传回值。它也“返回”受影响的行数,客户端 程序可以在SQL级别通过调用ROW_COUNT()函数获得这个数,从C中是调用the mysql_affected_rows() C API函数来获得。 20.2.7. BEGIN ... END复合语句 [begin_label:] BEGIN [statement_list] END [end_label] 存储子程序可以使用BEGIN ... END复合语句来包含多个语句。statement_list 代表一个或多个语句的列 表。statement_list之内每个语句都必须用分号(;)来结尾。 复合语句可以被标记。除非begin_label存在,否则end_label不能被给出,并且如果二者都存在,他们必须是同样 的。 请注意,可选的[NOT] ATOMIC子句现在还不被支持。这意味着在指令块的开始没有交互的存储点被设置,并 且在上下文中用到的BEGIN子句对当前交互动作没有影响。 使用多重语句需要客户端能发送包含语句定界符;的查询字符串。这个符号在命令行客户端被用delimiter命令来 处理。改变查询结尾定界符;(比如改变为//)使得; 可被用在子程序体中。 20.2.8. DECLARE语句 DECLARE语句被用来把不同项目局域到一个子程序:局部变量(请参阅20.2.9节,“存储程序中的变量”),条 件和处理程序(请参阅20.2.10节,“条件和处理程序”) 及光标(请参阅20.2.11节,“光 标”)。SIGNAL和RESIGNAL语句当前还不被支持。 DECLARE仅被用在BEGIN ... END复合语句里,并且必须在复合语句的开头,在任何其它语句之前。 光标必须在声明处理程序之前被声明,并且变量和条件必须在声明光标或处理程序之前被声明。 20.2.9. 存储程序中的变量 20.2.9.1. DECLARE局部变量 20.2.9.2. 变量SET语句 20.2.9.3. SELECT ... INTO语句 你可以在子程序中声明并使用变量。 20.2.9.1. DECLARE局部变量 DECLARE var_name[,...] type [DEFAULT value] 这个语句被用来声明局部变量。要给变量提供一个默认值,请包含一个DEFAULT子句。值可以被指定为一个表 达式,不需要为一个常数。如果没有DEFAULT子句,初始值为NULL。 局部变量的作用范围在它被声明的BEGIN ... END块内。它可以被用在嵌套的块中,除了那些用相同名字声明 变量的块。 20.2.9.2. 变量SET语句 SET var_name = expr [, var_name = expr] ... 在存储程序中的SET语句是一般SET语句的扩展版本。被参考变量可能是子程序内声明的变量,或者是全局服 务器变量。 在存储程序中的SET语句作为预先存在的SET语法的一部分来实现。这允许SET a=x, b=y, ...这样的扩展语法。 其中不同的变量类型(局域声明变量及全局和集体变量)可以被混合起来。这也允许把局部变量和一些只对系 统变量有意义的选项合并起来。在那种情况下,此选项被识别,但是被忽略了。 20.2.9.3. SELECT ... INTO语句 SELECT col_name[,...] INTO var_name[,...] table_expr 这个SELECT语法把选定的列直接存储到变量。因此,只有单一的行可以被取回。 SELECT id,data INTO x,y FROM test.t1 LIMIT 1; 注意,用户变量名在MySQL 5.1中是对大小写不敏感的。请参阅9.3节,“用户变量”。 重要: SQL变量名不能和列名一样。如果SELECT ... INTO这样的SQL语句包含一个对列的参考,并包含一个与 列相同名字的局部变量,MySQL当前把参考解释为一个变量的名字。例如,在下面的语句中,xname 被解释 为到xname variable 的参考而不是到xname column的: CREATE PROCEDURE sp1 (x VARCHAR(5)) BEGIN DECLARE xname VARCHAR(5) DEFAULT 'bob'; DECLARE newname VARCHAR(5); DECLARE xid INT; SELECT xname,id INTO newname,xid FROM table1 WHERE xname = xname; SELECT newname; END; 当这个程序被调用的时候,无论table.xname列的值是什么,变量newname将返回值‘bob’。 请参阅I.1节,“存储子程序和触发程序的限制”。 20.2.10. 条件和处理程序 20.2.10.1. DECLARE条件 20.2.10.2. DECLARE处理程序 特定条件需要特定处理。这些条件可以联系到错误,以及子程序中的一般流程控制。 20.2.10.1. DECLARE条件 DECLARE condition_name CONDITION FOR condition_value condition_value: SQLSTATE [VALUE] sqlstate_value | mysql_error_code 这个语句指定需要特殊处理的条件。它将一个名字和指定的错误条件关联起来。这个名字可以随后被用 在DECLARE HANDLER语句中。请参阅20.2.10.2节,“DECLARE处理程序”。 除了SQLSTATE值,也支持MySQL错误代码。 20.2.10.2. DECLARE处理程序 DECLARE handler_type HANDLER FOR condition_value[,...] sp_statement handler_type: CONTINUE | EXIT | UNDO condition_value: SQLSTATE [VALUE] sqlstate_value | condition_name | SQLWARNING | NOT FOUND | SQLEXCEPTION | mysql_error_code 这个语句指定每个可以处理一个或多个条件的处理程序。如果产生一个或多个条件,指定的语句被执行。 对一个CONTINUE处理程序,当前子程序的执行在执行处理程序语句之后继续。对于EXIT处理程序,当 前BEGIN...END复合语句的执行被终止。UNDO 处理程序类型语句还不被支持。 · SQLWARNING是对所有以01开头的SQLSTATE代码的速记。 · NOT FOUND是对所有以02开头的SQLSTATE代码的速记。 · SQLEXCEPTION是对所有没有被SQLWARNING或NOT FOUND捕获的SQLSTATE代码的速记。 除了SQLSTATE值,MySQL错误代码也不被支持。 例如: mysql> CREATE TABLE test.t (s1 int,primary key (s1)); Query OK, 0 rows affected (0.00 sec) mysql> delimiter // mysql> CREATE PROCEDURE handlerdemo () -> BEGIN -> DECLARE CONTINUE HANDLER FOR SQLSTATE '23000' SET @x2 = 1; -> SET @x = 1; -> INSERT INTO test.t VALUES (1); -> SET @x = 2; -> INSERT INTO test.t VALUES (1); -> SET @x = 3; -> END; -> // Query OK, 0 rows affected (0.00 sec) mysql> CALL handlerdemo()// Query OK, 0 rows affected (0.00 sec) mysql> SELECT @x// +------+ | @x | +------+ | 3 | +------+ 1 row in set (0.00 sec) 注意到,@x是3,这表明MySQL被执行到程序的末尾。如果DECLARE CONTINUE HANDLER FOR SQLSTATE '23000' SET @x2 = 1; 这一行不在,第二个INSERT因PRIMARY KEY强制而失败之后,MySQL可能已经采取默 认(EXIT)路径,并且SELECT @x可能已经返回2。 20.2.11. 光标 20.2.11.1.声明光标 20.2.11.2. 光标OPEN语句 20.2.11.3. 光标FETCH语句 20.2.11.4. 光标CLOSE语句 简单光标在存储程序和函数内被支持。语法如同在嵌入的SQL中。光标当前是不敏感的,只读的及不滚动的。 不敏感意为服务器可以活不可以复制它的结果表。 光标必须在声明处理程序之前被声明,并且变量和条件必须在声明光标或处理程序之前被声明。 例如: CREATE PROCEDURE curdemo() BEGIN DECLARE done INT DEFAULT 0; DECLARE a CHAR(16); DECLARE b,c INT; DECLARE cur1 CURSOR FOR SELECT id,data FROM test.t1; DECLARE cur2 CURSOR FOR SELECT i FROM test.t2; DECLARE CONTINUE HANDLER FOR SQLSTATE '02000' SET done = 1; OPEN cur1; OPEN cur2; REPEAT FETCH cur1 INTO a, b; FETCH cur2 INTO c; IF NOT done THEN IF b < c THEN INSERT INTO test.t3 VALUES (a,b); ELSE INSERT INTO test.t3 VALUES (a,c); END IF; END IF; UNTIL done END REPEAT; CLOSE cur1; CLOSE cur2; END 20.2.11.1.声明光标 DECLARE cursor_name CURSOR FOR select_statement 这个语句声明一个光标。也可以在子程序中定义多个光标,但是一个块中的每一个光标必须有唯一的名字。 SELECT语句不能有INTO子句。 20.2.11.2. 光标OPEN语句 OPEN cursor_name 这个语句打开先前声明的光标。 20.2.11.3. 光标FETCH语句 FETCH cursor_name INTO var_name [, var_name] ... 这个语句用指定的打开光标读取下一行(如果有下一行的话),并且前进光标指针。 20.2.11.4. 光标CLOSE语句 CLOSE cursor_name 这个语句关闭先前打开的光标。 如果未被明确地关闭,光标在它被声明的复合语句的末尾被关闭。 20.2.12. 流程控制构造 20.2.12.1. IF语句 20.2.12.2. CASE语句 20.2.12.3. LOOP语句 20.2.12.4. LEAVE语句 20.2.12.5. ITERATE语句 20.2.12.6. REPEAT语句 20.2.12.7. WHILE语句 IF, CASE, LOOP, WHILE, ITERATE, 及 LEAVE 构造被完全实现。 这些构造可能每个包含要么一个单独语句,要么是使用BEGIN ... END复合语句的一块语句。构造可以被嵌 套。 目前还不支持FOR循环。 20.2.12.1. IF语句 IF search_condition THEN statement_list [ELSEIF search_condition THEN statement_list] ... [ELSE statement_list] END IF IF实现了一个基本的条件构造。如果search_condition求值为真,相应的SQL语句列表被执行。如果没 有search_condition匹配,在ELSE子句里的语句列表被执行。statement_list可以包括一个或多个语句。 请注意,也有一个IF() 函数,它不同于这里描述的IF语句。请参阅12.2节,“控制流程函数”。 20.2.12.2. CASE语句 CASE case_value WHEN when_value THEN statement_list [WHEN when_value THEN statement_list] ... [ELSE statement_list] END CASE Or: CASE WHEN search_condition THEN statement_list [WHEN search_condition THEN statement_list] ... [ELSE statement_list] END CASE 存储程序的CASE语句实现一个复杂的条件构造。如果search_condition 求值为真,相应的SQL被执行。如果没 有搜索条件匹配,在ELSE子句里的语句被执行。 注意:这里介绍的用在存储程序里的CASE语句与12.2节,“控制流程函数”里描述的SQL CASE表达式 的CASE语句有轻微不同。这里的CASE语句不能有ELSE NULL子句,并且用END CASE替代END来终止。 20.2.12.3. LOOP语句 [begin_label:] LOOP statement_list END LOOP [end_label] LOOP允许某特定语句或语句群的重复执行,实现一个简单的循环构造。在循环内的语句一直重复直循环被退 出,退出通常伴随着一个LEAVE 语句。 LOOP语句可以被标注。除非begin_label存在,否则end_label不能被给出,并且如果两者都出现,它们必须是 同样的。 20.2.12.4. LEAVE语句 LEAVE label 这个语句被用来退出任何被标注的流程控制构造。它和BEGIN ... END或循环一起被使用。 20.2.12.5. ITERATE语句 ITERATE label ITERATE只可以出现在LOOP, REPEAT, 和WHILE语句内。ITERATE意思为:“再次循环。” 例如: CREATE PROCEDURE doiterate(p1 INT) BEGIN label1: LOOP SET p1 = p1 + 1; IF p1 < 10 THEN ITERATE label1; END IF; LEAVE label1; END LOOP label1; SET @x = p1; END 20.2.12.6. REPEAT语句 [begin_label:] REPEAT statement_list UNTIL search_condition END REPEAT [end_label] REPEAT语句内的语句或语句群被重复,直至search_condition 为真。 REPEAT 语句可以被标注。除非begin_label也存在,end_label才能被用,如果两者都存在,它们必须是一样 的。 例如: mysql> delimiter // mysql> CREATE PROCEDURE dorepeat(p1 INT) -> BEGIN -> SET @x = 0; -> REPEAT SET @x = @x + 1; UNTIL @x > p1 END REPEAT; -> END -> // Query OK, 0 rows affected (0.00 sec) mysql> CALL dorepeat(1000)// Query OK, 0 rows affected (0.00 sec) mysql> SELECT @x// +------+ | @x | +------+ | 1001 | +------+ 1 row in set (0.00 sec) 20.2.12.7. WHILE语句 [begin_label:] WHILE search_condition DO statement_list END WHILE [end_label] WHILE语句内的语句或语句群被重复,直至search_condition 为真。 WHILE语句可以被标注。除非begin_label也存在,end_label才能被用,如果两者都存在,它们必须是一样 的。 例如: CREATE PROCEDURE dowhile() BEGIN DECLARE v1 INT DEFAULT 5; WHILE v1 > 0 DO ... SET v1 = v1 - 1; END WHILE; END 20.3. 存储程序 、函数、触发程序及复制:常见问题 MySQL 5.1存储程序和函数对复制起作用吗? 是的,在存储程序和函数中被执行标准行为被从主MySQL服务器复制到从服务器。有少数限制,它们 在20.4节,“存储子程序和触发程序二进制日志功能”中详述。 在主服务器上创建的存储程序和函数可以被复制到从服务器上么? 是的,通过一般DDL语句执行的存储程序和函数,其在主服务器上的创建被复制到从服务器,所以目标 将存在两个服务器上。对存储程序和函数的ALTER 和DROP语句也被复制。 行为如何在已复制的存储程序和函数里发生? MySQL纪录每个发生在存储程序和函数里的DML事件,并复制这些单独的行为到从服务器。执行存储程 序和函数的切实调用不被复制。 对一起使用存储程序,函数和复制有什么特别的安全要求么? 是的,因为一个从服务器有权限来执行任何读自主服务器的二进制日志的语句,指定的安全约束因与复 制一起使用的存储程序和函数而存在。如果复制或二进制日志大体上是激活的(为point-in-time恢复的目 的),那么MySQL DBA 有两个安全选项可选: 任何想创建存储程序的用户必须被赋予SUPER权限。 作为选择,一个DBA可以设置log_bin_trust_routine_creators系统变量为1,它将会允许有标 准CREATE ROUTINE权限的人来创建一个存储程序和函数。 对复制存储程序和函数的行为有什么限制? 嵌入到存储程序中的不确定(随机)或时基行不能适当地复制。随机产生的结果,仅因其本性,是你可 预测的和不能被确实克隆的。因此,复制到从服务器的随机行为将不会镜像那些产生在主服务器上的。 注意, 声明存储程序或函数为DETERMINISTIC或者在log_bin_trust_routine_creators中设置系统变量为0 将会允许随即值操作被调用。 此外,时基行为不能在从服务器上重新产生,因为在存储程序中通过对复制使用的二进制日志来计时这 样的时基行为是不可重新产生的,因为该二进制日志仅纪录DML事件且不包括计时约束。 最后,在大型DML行为(如大批插入)中非交互表发生错误,该非交互表可能经历复制,在复制版的非 交互表中主服务器可以被部分地从DML行为更新。但是因为发生的那个错误,对从服务器没有更新。对 函数的DML行为,工作区将被用IGNORE关键词来执行,以便于在主服务器上导致错误的更新被忽略,并 且不会导致错误的更新被复制到从服务器。 上述的限制会影响MySQL作 point-in-time恢复的能力吗? 影响复制的同一限制会影响point-in-time恢复。 MySQL要做什么来改正前述的限制呢? 将来发行的MySQL预期有一个功能去选择复制该如何被处理: 基于语句的复制(当前实现)。 行级别复制(它将解决所有早先描述的限制)。 触发程序对复制起作用么? MySQL 5.1中的触发程序和复制象在大多数其它数据库引擎中一样工作,在那些引擎中,通过触发程序在 主服务器上执行的行为不被复制到从服务器。取而代之的是,位于主MySQL服务器的表中的触发程序需 要在那些存在于任何MySQL从服务器上的表内被创建,以便于触发程序可以也可以在从服务器上被激 活。 一个行为如何通过从主服务器上复制到从服务器上的触发程序来执行呢? 首先,主服务器上的触发程序必须在从服务器上重建。一旦重建了,复制流程就象其它参与到复制中的 标准DML语句一样工作。例如:考虑一个已经插入触发程序AFTER的EMP表,它位于主MySQL服务器 上。同样的EMP表和AFTER插入触发程序也存在于从服务器上。复制流程可能是: 1. 对EMP做一个INSERT语句。 2. EMP上的AFTER触发程序激活。 3. INSERT语句被写进二进制日志。 4. 从服务器上的复制拾起INSERT语句给EMP表,并在从服务器上执行它。 5. 位于从服务器EMP上的AFTER触发程序激活。 20.4. 存储子程序和触发程序的二进制日志功能 ,这一节介绍MySQL 5.1如何考虑二进制日志功能来处理存储子程序(程序和函数) 。这一节也适用于触发程 序。 二进制日志包含修改数据库内容的SQL语句的信息。这个信息以描述修改的事件的形式保存起来。 二进制日志有两个重要目的: · 复制的基础是主服务器发送包含在二进制日志里的事件到从服务器,从服务器执行这些事件来造成与对 主服务器造成的同样的数据改变,请参阅6.2节,“复制概述”。 · 特定的数据恢复操作许要使用二进制日志。备份的文件被恢复之后,备份后纪录的二进制日志里的事件 被重新执行。这些事件把数据库带从备份点的日子带到当前。请参阅5.9.2.2节,“使用备份恢复”。 MySQL中,以存储子程序的二进制日志功能引发了很多问题,这些在下面讨论中列出,作为参考信息。 除了要另外注意的之外,这些谈论假设你已经通过用--log-bin选项启动服务器允许了二进制日志功能。(如果 二进制日志功能不被允许,复制将不可能,为数据恢复的二进制日志也不存在。)请参阅5.11.3节,“二进制日 志”。 对存储子程序语句的二进制日志功能的特征在下面列表中描述。一些条目指出你应该注意到的问题。但是在一 些情况下,有你可以更改的妇五七设置或你可以用来处理它们的工作区。 · CREATE PROCEDURE, CREATE FUNCTION, ALTER PROCEDURE,和ALTER FUNCTION 语句被写进二进 制日志,CALL, DROP PROCEDURE, 和DROP FUNCTION 也一样。 尽管如此,对复制有一个安全暗示:要创建一个子程序,用户必须有CREATE ROUTINE权限,但有这个权限的 用户不能写一个子程序在从服务器上执行任何操作。因为在从服务器上的SQL线程用完全权限来运行。例如, 如果主服务器和从服务器分别有服务器ID值1和2,在主服务器上的用户可能创建并调用如下一个程序: mysql> delimiter // mysql> CREATE PROCEDURE mysp () -> BEGIN -> IF @@server_id=2 THEN DROP DATABASE accounting; END IF; -> END; -> // mysql> delimiter ; mysql> CALL mysp(); CREATE PROCEDURE和CALL语句将被写进二进制日志,所以从服务器将执行它们。因为从SQL线程有完全权 限,它将移除accounting数据库。 要使允许二进制日志功能的服务器避免这个危险,MySQL 5.1已经要求存储程序和函数的创建者除了通常需要 的CREATE ROUTINE的权限外,还必须有SUPER 权限。类似地,要使用ALTER PROCEDURE或ALTER FUNCTION,除了ALTER ROUTINE权限外你必须有SUPER权限。没有SUPER权限,将会发生一个错误: ERROR 1419 (HY000): You do not have the SUPER privilege and binary logging is enabled (you *might* want to use the less safe log_bin_trust_routine_creators variable) 你可能不想强制要求子程序创建者必须有SUPER权限。例如,你系统上所有有CREATE ROUTINE权限的用户可 能是有经验的应用程序开发者。要禁止掉对SUPER权限的要求,设置log_bin_trust_routine_creators 全局系统 变量为1。默认地,这个变量值为0,但你可以象这样改变这样: mysql> SET GLOBAL log_bin_trust_routine_creators = 1; 你也可以在启动服务器之时用--log-bin-trust-routine-creators选项来设置允许这个变量。 如果二进制日志功能不被允许,log_bin_trust_routine_creators 没有被用上,子程序创建需要SUPER权限。 · 一个执行更新的非确定子程序是不可重复的,它能有两个不如意的影响: o 它会使得从服务器不同于主服务器。 - 恢复的数据与原始数据不同。 要解决这些问题,MySQL强制做下面要求:在主服务器上,除非子程序被声明为确定性的或者不更改数据,否 则创建或者替换子程序将被拒绝。这意味着当你创建一个子程序的时候,你必须要么声明它是确定性的,要么 它不改变数据。两套子程序特征在这里适用: - DETERMINISTIC和NOT DETERMINISTIC指出一个子程序是否对给定的输入总是产生同样的结果。如果 没有给定任一特征,默认是NOT DETERMINISTIC,所以你必须明确指定DETERMINISTIC来声明一个 子程序 是确定性的。 使用NOW() 函数(或它的同义)或者RAND() 函数不是必要地使也一个子程序非确定性。对NOW()而言,二进 制日志包括时间戳并正确复制。RAND()只要在一个子程序内被调用一次也可以正确复制。(你可以认为子程 序执行时间戳和随机数种子作为毫无疑问地输入,它们在主服务器和从服务器上是一样的。) - CONTAINS SQL, NO SQL, READS SQL DATA, 和 MODIFIES SQL数据提供子程序是读还是写数据的信 息。无论NO SQL 还是READS SQL DATA i都指出,子程序没有改变数据,但你必须明白地指明这些中的一 个,因为如果任何这些特征没有被给出, 默认的特征是CONTAINS SQL。 默认地,要一个CREATE PROCEDURE 或 CREATE FUNCTION 语句被接受,DETERMINISTIC 或 NO SQL与READS SQL DATA 中的一个必须明白地指定,否则会产生如下错误: ERROR 1418 (HY000): This routine has none of DETERMINISTIC, NO SQL, or READS SQL DATA in its declaration and binary logging is enabled (you *might* want to use the less safe log_bin_trust_routine_creators variable) 如果设置log_bin_trust_routine_creators 为1, 移除对子程序必须是确定的或不修改数据的要求。 注意,子程序本性的评估是基于创建者的“诚实度” :MySQL不检查声明为确定性的子程序是否不含产生非确定 性结果的语句。 · 如果子程序返回无错,CALL语句被写进二进制日志,否则就不写。当一个子程序修改数据失败了,你 会得到这样的警告: · ERROR 1417 (HY000): A routine failed and has neither NO SQL nor · READS SQL DATA in its declaration and binary logging is enabled; if · non-transactional tables were updated, the binary log will miss their · changes 这个记日志行为潜在地导致问题.如果一个子程序部分地修改一个非交互表(比如一个MyISAM表able)并且返 回一个错误,二进制日志将反映这些变化。要防止这种情况,你应该在子程序中使用交互表并且在交互动作内 修改表。 在一个子程序内,如果你在INSERT, DELETE, 或者UPDATE里使用IGNORE关键词来忽略错误,可能发生一个 部分更新,但没有错误产生。这样的语句被记录日志,且正常复制。 · 如果一个存储函数在一个如SELECT这样不修改数据的语句内被调用,即使函数本身更改数据,函数的 执行也将不被写进二进制日志里。这个记录日志的行为潜在地导致问题。假设函数myfunc()如下定义: · CREATE FUNCTION myfunc () RETURNS INT · BEGIN · INSERT INTO t (i) VALUES(1); · RETURN 0; · END; 按照上面定义,下面的语句修改表t,因为myfunc()修改表t, 但是语句不被写进二进制日志,因为它是一 个SELECT语句: SELECT myfunc(); 对这个问题的工作区将调用在做更新的语句里做更新的函数。注意,虽然DO语句有时为了其估算表达式的副 效应而被执行,DO在这里不是一个工作区,因为它不被写进二进制日志。 · 在一个子程序内执行的语句不被写进二进制日志。假如你发布下列语句: · CREATE PROCEDURE mysp INSERT INTO t VALUES(1); · CALL mysp; 对于这个例子来说,CREATE PROCEDURE 和CALL语句出现在二进制日志里,但INSERT语句并未出现。 · 在从服务器上,当决定复制哪个来自主服务器的事件时,下列限制被应用:--replicate-*-table规则不适 用于CALL语句或子程序内的语句:在这些情况下,总是返回“复制!” 触发程序类似于存储函数,所以前述的评论也适用于触发程序,除了下列情况: CREATE TRIGGER没有可选 的DETERMINISTIC特征,所以触发程序被假定为总是确定性的。然而,这个假设在一些情况下是非法的。比 如,UUID()函数是非确定性的(不能复制)。你应该小心在触发程序中使用这个函数。 触发程序目前不能更新表,但是在将来会支持。因为这个原因,如果你没有SUPER权限 且log_bin_trust_routine_creators 被设为0,得到的错误信息类似于存储子程序与CREATE TRIGGER产生的错 误信息。 在本节中叙述的问题来自发生在SQL语句级别的二进制日志记录的事实。未来发行的MySQL期望能实现行级的 二进制日志记录,记录发生在更细致的级别并且指出哪个改变作为执行SQL的结果对单个记录而做。 这是MySQL参考手册的翻译版本,关于MySQL参考手册,请访问dev.mysql.com。原始参考手册为英文版,与 英文版参考手册相比,本翻译版可能不是最新的 C API函数概述 这里归纳了C API可使用的函数,并在下一节详细介绍了它们。请参见25.2.3节,“C API函数描述”。 函数描述 mysql_affected_rows() 返回上次UPDATE、DELETE或INSERT查询更改/删除/插入的行数。 mysql_autocommit() 切换 autocommit模式,ON/OFF mysql_change_user() 更改打开连接上的用户和数据库。 mysql_charset_name() 返回用于连接的默认字符集的名称。 mysql_close() 关闭服务器连接。 mysql_commit() 提交事务。 mysql_connect() 连接到MySQL服务器。该函数已不再被重视,使用mysql_real_connect()取代。 mysql_create_db() 创建数据库。该函数已不再被重视,使用SQL语句CREATE DATABASE取而代之。 mysql_data_seek() 在查询结果集中查找属性行编号。 mysql_debug() 用给定的字符串执行DBUG_PUSH。 mysql_drop_db() 撤销数据库。该函数已不再被重视,使用SQL语句DROP DATABASE取而代之。 mysql_dump_debug_info() 让服务器将调试信息写入日志。 mysql_eof() 确定是否读取了结果集的最后一行。该函数已不再被重视,可以使 用mysql_errno()或mysql_error()取而代之。 mysql_errno() 返回上次调用的MySQL函数的错误编号。 mysql_error() 返回上次调用的MySQL函数的错误消息。 mysql_escape_string() 为了用在SQL语句中,对特殊字符进行转义处理。 mysql_fetch_field() 返回下一个表字段的类型。 mysql_fetch_field_direct() 给定字段编号,返回表字段的类型。 mysql_fetch_fields() 返回所有字段结构的数组。 mysql_fetch_lengths() 返回当前行中所有列的长度。 mysql_fetch_row() 从结果集中获取下一行 mysql_field_seek() 将列光标置于指定的列。 mysql_field_count() 返回上次执行语句的结果列的数目。 mysql_field_tell() 返回上次mysql_fetch_field()所使用字段光标的位置。 mysql_free_result() 释放结果集使用的内存。 mysql_get_client_info() 以字符串形式返回客户端版本信息。 mysql_get_client_version() 以整数形式返回客户端版本信息。 mysql_get_host_info() 返回描述连接的字符串。 mysql_get_server_version() 以整数形式返回服务器的版本号。 mysql_get_proto_info() 返回连接所使用的协议版本。 mysql_get_server_info() 返回服务器的版本号。 mysql_info() 返回关于最近所执行查询的信息。 mysql_init() 获取或初始化MYSQL结构。 mysql_insert_id() 返回上一个查询为AUTO_INCREMENT列生成的ID。 mysql_kill() 杀死给定的线程。 mysql_library_end() 最终确定MySQL C API库。 mysql_library_init() 初始化MySQL C API库。 mysql_list_dbs() 返回与简单正则表达式匹配的数据库名称。 mysql_list_fields() 返回与简单正则表达式匹配的字段名称。 mysql_list_processes() 返回当前服务器线程的列表。 mysql_list_tables() 返回与简单正则表达式匹配的表名。 mysql_more_results() 检查是否还存在其他结果。 mysql_next_result() 在多语句执行过程中返回/初始化下一个结果。 mysql_num_fields() 返回结果集中的列数。 mysql_num_rows() 返回结果集中的行数。 mysql_options() 为mysql_connect()设置连接选项。 mysql_ping() 检查与服务器的连接是否工作,如有必要重新连接。 mysql_query() 执行指定为“以Null终结的字符串”的SQL查询。 mysql_real_connect() 连接到MySQL服务器。 mysql_real_escape_string() 考虑到连接的当前字符集,为了在SQL语句中使用,对字符串中的特殊字符进行转义处理。 mysql_real_query() 执行指定为计数字符串的SQL查询。 mysql_refresh() 刷新或复位表和高速缓冲。 mysql_reload() 通知服务器再次加载授权表。 mysql_rollback() 回滚事务。 mysql_row_seek() 使用从mysql_row_tell()返回的值,查找结果集中的行偏移。 mysql_row_tell() 返回行光标位置。 mysql_select_db() 选择数据库。 mysql_server_end() 最终确定嵌入式服务器库。 mysql_server_init() 初始化嵌入式服务器库。 mysql_set_server_option() 为连接设置选项(如多语句)。 mysql_sqlstate() 返回关于上一个错误的SQLSTATE错误代码。 mysql_shutdown() 关闭数据库服务器。 mysql_stat() 以字符串形式返回服务器状态。 mysql_store_result() 检索完整的结果集至客户端。 mysql_thread_id() 返回当前线程ID。 mysql_thread_safe() 如果客户端已编译为线程安全的,返回1。 mysql_use_result() 初始化逐行的结果集检索。 mysql_warning_count() 返回上一个SQL语句的告警数。 与MySQL交互时,应用程序应使用该一般性原则: 1. 通过调用mysql_library_init(),初始化MySQL库。库可以是mysqlclient C客户端库,或mysqld嵌入式服务器库,具体情况 取决于应用程序是否与“-libmysqlclient”或“-libmysqld”标志链接。 2. 通过调用mysql_init()初始化连接处理程序,并通过调用mysql_real_connect()连接到服务器。 3. 发出SQL语句并处理其结果。(在下面的讨论中,详细介绍了使用它的方法)。 4. 通过调用mysql_close(),关闭与MySQL服务器的连接。 5. 通过调用mysql_library_end(),结束MySQL库的使用。 调用mysql_library_init()和mysql_library_end()的目的在于,为MySQL库提供恰当的初始化和结束处理。对于与客户端库链接的 应用程序,它们提供了改进的内存管理功能。如果不调用mysql_library_end(),内存块仍将保持分配状态(这不会增加应用程 序使用的内存量,但某些内存泄漏检测器将抗议它)。对于与嵌入式服务器链接的应用程序,这些调用会启动并停止服务器。 mysql_library_init()和mysql_library_end()实际上是#define符号,这类符号使得它们等效 于mysql_server_init()和mysql_server_end(),但其名称更清楚地指明,无论应用程序使用的是mysqlclient或mysqld库,启动或 结束MySQL库时,应调用它们。对于早期的MySQL版本,可调用mysql_server_init()和mysql_server_end()取而代之。 如果愿意,可省略对mysql_library_init()的调用,这是因为,必要时,mysql_init()会自动调用它。 要想连接到服务器,可调用mysql_init()来初始化连接处理程序,然后用该处理程序(以及其他信息,如主机名、用户名和密 码)调用mysql_real_connect()。建立连接后,在低于5.0.3版的API中,mysql_real_connect()会将再连接标志(MYSQL结构的 一部分)设置为1,或在较新的版本中,将其设置为0。对于该标志,值“1”指明,如果因连接丢失而无法执行语句,放弃之前, 会尝试再次连接到服务器。从MySQL 5.0.13开始,可以在mysql_options()上使用MYSQL_OPT_RECONNECT选项,以控制再连 接行为。完成连接后,调用mysql_close()中止它。 当连接处于活动状态时,客户端或许会使用mysql_query()或mysql_real_query()向服务器发出SQL查询。两者的差别在 于,mysql_query()预期的查询为指定的、由Null终结的字符串,而mysql_real_query()预期的是计数字符串。如果字符串包含二 进制数据(其中可能包含Null字节),就必须使用mysql_real_query()。 对于每个非SELECT查询(例如INSERT、UPDATE、DELETE),通过调用mysql_affected_rows(),可发现有多少行已被改 变(影响)。 对于SELECT查询,能够检索作为结果集的行。注意,某些语句因其返回行,类似与SELECT。包 括SHOW、DESCRIBE和EXPLAIN。应按照对待SELECT语句的方式处理它们。 客户端处理结果集的方式有两种。一种方式是,通过调用mysql_store_result(),一次性地检索整个结果集。该函数能从服务器 获得查询返回的所有行,并将它们保存在客户端。第二种方式是针对客户端的,通过调用mysql_use_result(),对“按行”结果集 检索进行初始化处理。该函数能初始化检索结果,但不能从服务器获得任何实际行。 在这两种情况下,均能通过调用mysql_fetch_row()访问行。通过mysql_store_result(),mysql_fetch_row()能够访问以前从服 务器获得的行。通过mysql_use_result(),mysql_fetch_row()能够实际地检索来自服务器的行。通过调 用mysql_fetch_lengths(),能获得关于各行中数据大小的信息。 完成结果集操作后,请调用mysql_free_result()释放结果集使用的内存。 这两种检索机制是互补的。客户端程序应选择最能满足其要求的方法。实际上,客户端最常使用的是mysql_store_result()。 mysql_store_result()的1个优点在于,由于将行全部提取到了客户端上,你不仅能连续访问行,还能使 用mysql_data_seek()或mysql_row_seek()在结果集中向前或向后移动,以更改结果集内当前行的位置。通过调 用mysql_num_rows(),还能发现有多少行。另一方面,对于大的结果集,mysql_store_result()所需的内存可能会很大,你很可 能遇到内存溢出状况。 mysql_use_result()的1个优点在于,客户端所需的用于结果集的内存较少,原因在于,一次它仅维护一行(由于分配开销较 低,mysql_use_result()能更快)。它的缺点在于,你必须快速处理每一行以避免妨碍服务器,你不能随机访问结果集中的行 (只能连续访问行),你不知道结果集中有多少行,直至全部检索了它们为止。不仅如此,即使在检索过程中你判定已找到所 寻找的信息,也必须检索所有的行。 通过API,客户端能够恰当地对查询作出响应(仅在必要时检索行),而无需知道查询是否是SELECT查询。可以在每 次mysql_query()或mysql_real_query()后,通过调用mysql_store_result()完成该操作。如果结果集调用成功,查询为SELECT, 而且能够读取行。如果结果集调用失败,可调用mysql_field_count()来判断结果是否的确是所预期的。如 果mysql_field_count()返回0,查询不返回数据(表明它是INSERT、UPDATE、DELETE等),而且不返回行。如 果mysql_field_count()是非0值,查询应返回行,但没有返回行。这表明查询是失败了的SELECT。关于如何实现该操作的示例, 请参见关于mysql_field_count()的介绍。 无论是mysql_store_result()还是mysql_use_result(),均允许你获取关于构成结果集的字段的信息(字段数目,它们的名称和类 型等)。通过重复调用mysql_fetch_field(),可以按顺序访问行内的字段信息,或者,通过调用mysql_fetch_field_direct(),能 够在行内按字段编号访问字段信息。通过调用mysql_field_seek(),可以改变当前字段的光标位置。对字段光标的设置将影响后 续的mysql_fetch_field()调用。此外,你也能通过调用mysql_fetch_fields(),一次性地获得关于字段的所有信息。 为了检测和通报错误,MySQL提供了使用mysql_errno()和mysql_error()函数访问错误信息的机制。它们能返回关于最近调用的 函数的错误代码或错误消息,最近调用的函数可能成功也可能失败,这样,你就能判断错误是在何时出现的,以及错误是什 C API预处理语句函数概述 在此归纳了预处理语句处理功能可使用的函数,并在后面的章节中详细介绍了它。请参见25.2.7节,“C API预处理语句函数描 述”。 函数描述 mysql_stmt_affected_rows() 返回由预处理语句UPDATE、DELETE或INSERT变更、删除或插入的行数目。 mysql_stmt_attr_get() 获取预处理语句属性的值。 mysql_stmt_attr_set() 设置预处理语句的属性。 mysql_stmt_bind_param() 将应用程序数据缓冲与预处理SQL语句中的参数标记符关联起来。 mysql_stmt_bind_result() 将应用程序数据缓冲与结果集中的列关联起来。 mysql_stmt_close() 释放预处理语句使用的内存。 mysql_stmt_data_seek() 寻找语句结果集中的任意行编号。 mysql_stmt_errno() 返回上次语句执行的错误编号。 mysql_stmt_error() 返回上次语句执行的错误消息。 mysql_stmt_execute() 执行预处理语句。 mysql_stmt_fetch() 从结果集获取数据的下一行,并返回所有绑定列的数据。 mysql_stmt_fetch_column() 获取结果集当前行中某列的数据。 mysql_stmt_field_count() 对于最近的语句,返回结果行的数目。 mysql_stmt_free_result() 释放分配给语句句柄的资源。 mysql_stmt_init() 为MYSQL_STMT结构分配内存并初始化它。 mysql_stmt_insert_id() 对于预处理语句的AUTO_INCREMENT列,返回生成的ID。 mysql_stmt_num_rows() 从语句缓冲结果集返回总行数。 mysql_stmt_param_count() 返回预处理SQL语句中的参数数目。 mysql_stmt_param_metadata() 返回结果集的参数元数据。 mysql_stmt_prepare() 为执行操作准备SQL字符串。 mysql_stmt_reset() 复位服务器中的语句缓冲区。 mysql_stmt_result_metadata() 以结果集形式返回预处理语句元数据。 mysql_stmt_row_seek() 使用从mysql_stmt_row_tell()返回的值,查找语句结果集中的行偏移。 mysql_stmt_row_tell() 返回语句行光标位置。 mysql_stmt_send_long_data() 将程序块中的长数据发送到服务器。 mysql_stmt_sqlstate() 返回关于上次语句执行的SQLSTATE错误代码。 mysql_stmt_store_result() 将完整的结果集检索到客户端。 调用mysql_stmt_init()以创建语句句柄,然后调用mysql_stmt_prepare准备语句,调用mysql_stmt_bind_param()提供参数数 据,并调用mysql_stmt_execute()执行语句。通过更改mysql_stmt_bind_param()提供的相应缓冲区中的参数值,可重复执 行mysql_stmt_execute()。 如果语句是SELECT或任何其他能生成结果集的语句,mysql_stmt_prepare()也会通 过mysql_stmt_result_metadata()以MYSQL_RES结果集的形式返回结果集元数据信息。 你可以使用mysql_stmt_bind_result()提供结果缓冲,以便mysql_stmt_fetch()能自动将数据返回给这些缓冲。这是一种按行获 取方式。 此外,你也能使用mysql_stmt_send_long_data()将程序块中的文本或二进制数据发送到服务器。请参 见25.2.7.25节,“mysql_stmt_send_long_data()”。 完成语句执行后,必须使用mysql_stmt_close()关闭语句句柄,以便与之相关的所有资源均能被释放。 如果通过调用mysql_stmt_result_metadata()获得了SELECT语句的结果集元数据,也应使用mysql_free_result()释放元数据。 执行步骤 要想准备和执行语句,应用程序必须采取下述步骤: 1. 用msyql_stmt_init()创建预处理语句句柄。要想在服务器上准备预处理语句,可调用mysql_stmt_prepare(),并为其传递包 含SQL语句的字符串。 2. 如果语句生成了结果集,调用mysql_stmt_result_metadata()以获得结果集元数据。虽然与包含查询返回列的结果集不同, 该元数据本身也采用了结果集的形式。元数据结果集指明了结果中包含多少列,并包含每一列的信息。 3. 使用mysql_stmt_bind_param()设置任何参数的值。必须设置所有参数。否则,语句执行将返回错误,或生成无法预料的 结果。 4. 调用mysql_stmt_execute()执行语句。 5. 如果语句生成了结果集,捆绑数据缓冲,通过调用mysql_stmt_bind_result(),检索行值。 6. 通过重复调用mysql_stmt_fetch(),按行将数据提取到缓冲区,直至未发现更多行为止。 7. 通过更改参数值并再次执行语句,重复步骤3到步骤6。 调用mysql_stmt_prepare()时,MySQL客户端/服务器协议将执行下述动作: · 服务器解析语句,并通过赋值语句ID将OK状态发回客户端。此外,如果它是面向结果集的语句,还将发送总的参数数 目,列计数和元数据。在此调用过程中,服务器将检查语句的所有语法和语义。 · 客户端采用该语句ID用于进一步操作,以便服务器能从其语句池中识别语句。 调用mysql_stmt_execute()时,MySQL客户端/服务器协议将执行下述动作: · 客户端使用语句句柄,并将参数数据发送到服务器。 · 服务器使用由客户端提供的ID来识别语句,用新提供的数据替换参数标记符,并执行语句。如果语句生成了结果集,服 务器将数据发回客户端。否则,服务器会将发送OK状态,以及总的变更、删除和插入行数。 调用mysql_stmt_fetch()时,MySQL客户端/服务器协议将执行下述动作: · 客户端按行从信息包读取数据,并通过执行必要的转换操作将其放入应用程序数据缓冲中。如果应用程序的缓冲类型与 服务器返回的字段类型相同,转换十分简明。 如果出现了错误,可分别使用mysql_stmt_errno()、mysql_stmt_error()和mysql_stmt_sqlstate()获取语句错误代码、错误消息 和SQLSTATE值。 预处理语句日志功能 对于与mysql_stmt_prepare()和mysql_stmt_execute() C API函数一起执行的预处理语句,服务器会将“准备”和“执行”行写入一 般查询日志,以便你能了解语句是在何时准备和执行的。 假定按下述方式准备和执行了语句: 1. 调用mysql_stmt_prepare()以准备语句字符串"SELECT ?"。 2. 调用mysql_stmt_bind_param()将值“3”绑定到预处理语句中的参数。 3. 调用mysql_stmt_execute(),执行预处理语句。 上述调用的结果是,服务器将下述行写入一般查询日志: Prepare [1] SELECT ? Execute [1] SELECT 3 日志中的每个“准备”和“执行”行均具有[n]语句ID标识,这样,你就能跟踪已记录的预处理语句。N是正整数。对于客户端,如 果同时有多个活动的预处理语句,n可能会大于1。替换了“?”参数的数据值后,每个“执行”行将显示一条预处理语句。 版本说明:在MySQL 4.1.10之前,显示的“准备”行无[n]标识。在MySQL 4.1.10之前,不显示“执行”行。 C API线程函数介绍 25.2.11.1. my_init() 25.2.11.2. mysql_thread_init() 25.2.11.3. mysql_thread_end() 25.2.11.4. mysql_thread_safe() 当你打算创建线程客户端时,需要使用下述函数。请参见25.2.15节,“如何生成线程式客户端”。 25.2.11.1. my_init() void my_init(void) 描述 调用任何MySQL函数之前,需要在程序中调用该函数。它将初始化MySQL所需的某些全局变量。如果你正在使用线程安全客户 端库,它还能为该线程调用mysql_thread_init()。 通过mysql_init()、mysql_library_init()、mysql_server_init()和mysql_connect(),可自动调用该函数。 返回值 无。 25.2.11.2. mysql_thread_init() my_bool mysql_thread_init(void) 描述 对于每个创建的线程,需要调用该函数来初始化与线程相关的变量。 它可由my_init()和mysql_connect()自动调用。 返回值 如果成功,返回0,如果出现错误,返回非0值。 25.2.11.3. mysql_thread_end() void mysql_thread_end(void) 描述 调用pthread_exit()来释放mysql_thread_init()分配的内存之前,需要调用该函数。 注意,该函数不会被客户端库自动调用。必须明确调用它以避免内存泄漏。 返回值 无。 25.2.11.4. mysql_thread_safe() unsigned int mysql_thread_safe(void) 描述 该函数指明了客户端是否编译为线程安全的。 返回值 如果客户端是线程安全的,返回1,否则返回0 服务器错误代码和消息 服务器错误信息来自下述源文件: · 错误消息信息列在share/errmsg.txt文件中。“%d”和“%s”分别代表编号和字符串,显示时,它们将被消 息值取代。 · 错误值列在share/errmsg.txt文件中,用于生成include/mysqld_error.h和include/mysqld_ername.h MySQL源文件中的定义。 · SQLSTATE值列在share/errmsg.txt文件中,用于生成include/sql_state.h MySQL源文件中的定义。 由于更新很频繁,这些文件中可能包含这里未列出的额外错误消息。 · 错误:1000 SQLSTATE: HY000 (ER_HASHCHK) 消息:hashchk · 错误:1001 SQLSTATE: HY000 (ER_NISAMCHK) 消息:isamchk · 错误:1002 SQLSTATE: HY000 (ER_NO) 消息:NO · 错误:1003 SQLSTATE: HY000 (ER_YES) 消息:YES · 错误:1004 SQLSTATE: HY000 (ER_CANT_CREATE_FILE) 消息:无法创建文件'%s' (errno: %d) · 错误:1005 SQLSTATE: HY000 (ER_CANT_CREATE_TABLE) 消息:无法创建表'%s' (errno: %d) · 错误:1006 SQLSTATE: HY000 (ER_CANT_CREATE_DB) 消息:无法创建数据库'%s' (errno: %d) · 错误:1007 SQLSTATE: HY000 (ER_DB_CREATE_EXISTS) 消息:无法创建数据库'%s',数据库已存在。 · 错误:1008 SQLSTATE: HY000 (ER_DB_DROP_EXISTS) 消息:无法撤销数据库'%s',数据库不存在。 · 错误:1009 SQLSTATE: HY000 (ER_DB_DROP_DELETE) 消息:撤销数据库时出错(无法删除'%s',errno: %d) · 错误:1010 SQLSTATE: HY000 (ER_DB_DROP_RMDIR) 消息:撤销数据库时出错(can't rmdir '%s', errno: %d) · 错误:1011 SQLSTATE: HY000 (ER_CANT_DELETE_FILE) 消息:删除'%s'时出错 (errno: %d) · 错误:1012 SQLSTATE: HY000 (ER_CANT_FIND_SYSTEM_REC) 消息:无法读取系统表中的记录。 · 错误:1013 SQLSTATE: HY000 (ER_CANT_GET_STAT) 消息:无法获取'%s'的状态(errno: %d) · 错误:1014 SQLSTATE: HY000 (ER_CANT_GET_WD) 消息:无法获得工作目录(errno: %d) · 错误:1015 SQLSTATE: HY000 (ER_CANT_LOCK) 消息:无法锁定文件(errno: %d) · 错误:1016 SQLSTATE: HY000 (ER_CANT_OPEN_FILE) 消息:无法打开文件:'%s' (errno: %d) · 错误:1017 SQLSTATE: HY000 (ER_FILE_NOT_FOUND) 消息:无法找到文件: '%s' (errno: %d) · 错误:1018 SQLSTATE: HY000 (ER_CANT_READ_DIR) 消息:无法读取'%s'的目录 (errno: %d) · 错误:1019 SQLSTATE: HY000 (ER_CANT_SET_WD) 消息:无法为'%s'更改目录 (errno: %d) · 错误:1020 SQLSTATE: HY000 (ER_CHECKREAD) 消息:自上次读取以来表'%s'中的记录已改变。 · 错误:1021 SQLSTATE: HY000 (ER_DISK_FULL) 消息:磁盘满(%s);等待某人释放一些空间... · 错误:1022 SQLSTATE: 23000 (ER_DUP_KEY) 消息:无法写入;复制表'%s'的键。 · 错误:1023 SQLSTATE: HY000 (ER_ERROR_ON_CLOSE) 消息:关闭'%s'时出错 (errno: %d) · 错误:1024 SQLSTATE: HY000 (ER_ERROR_ON_READ) 消息:读取文件'%s'时出错 (errno: %d) · 错误:1025 SQLSTATE: HY000 (ER_ERROR_ON_RENAME) 消息:将'%s'重命名为'%s'时出错 (errno: %d) · 错误:1026 SQLSTATE: HY000 (ER_ERROR_ON_WRITE) 消息:写入文件'%s'时出错 (errno: %d) · 错误:1027 SQLSTATE: HY000 (ER_FILE_USED) 消息:'%s'已锁定,拒绝更改。 · 错误:1028 SQLSTATE: HY000 (ER_FILSORT_ABORT) 消息:分类失败 · 错误:1029 SQLSTATE: HY000 (ER_FORM_NOT_FOUND) 消息:对于'%s',视图'%s'不存在。 · 错误:1030 SQLSTATE: HY000 (ER_GET_ERRNO) 消息:从存储引擎中获得错误%d。 · 错误:1031 SQLSTATE: HY000 (ER_ILLEGAL_HA) 消息:关于'%s'的表存储引擎不含该选项。 · 错误:1032 SQLSTATE: HY000 (ER_KEY_NOT_FOUND) 消息:无法在'%s'中找到记录。 · 错误:1033 SQLSTATE: HY000 (ER_NOT_FORM_FILE) 消息:文件中的不正确信息:'%s' · 错误:1034 SQLSTATE: HY000 (ER_NOT_KEYFILE) 消息:对于表'%s', 键文件不正确,请尝试修复。 · 错误:1035 SQLSTATE: HY000 (ER_OLD_KEYFILE) 消息:旧的键文件,对于表'%s',请修复之! · 错误:1036 SQLSTATE: HY000 (ER_OPEN_AS_READONLY) 消息:表'%s'是只读的。 · 错误:1037 SQLSTATE: HY001 (ER_OUTOFMEMORY) 消息:内存溢出,重启服务器并再次尝试(需要%d字节)。 · 错误:1038 SQLSTATE: HY001 (ER_OUT_OF_SORTMEMORY) 消息:分类内存溢出,增加服务器的分类缓冲区大小。 · 错误:1039 SQLSTATE: HY000 (ER_UNEXPECTED_EOF) 消息:读取文件'%s'时出现意外EOF (errno: %d) · 错误:1040 SQLSTATE: 08004 (ER_CON_COUNT_ERROR) 消息:连接过多。 · 错误:1041 SQLSTATE: HY000 (ER_OUT_OF_RESOURCES) 消息:内存溢出,请检查是否mysqld或其他进程使用了所有可用内存,如不然,或许应使用'ulimit'允 许mysqld使用更多内存,或增加交换空间的大小。 · 错误:1042 SQLSTATE: 08S01 (ER_BAD_HOST_ERROR) 消息:无法获得该地址给出的主机名。 · 错误:1043 SQLSTATE: 08S01 (ER_HANDSHAKE_ERROR) 消息:不良握手 · 错误:1044 SQLSTATE: 42000 (ER_DBACCESS_DENIED_ERROR) 消息:拒绝用户'%s'@'%s'访问数据库'%s'。 · 错误:1045 SQLSTATE: 28000 (ER_ACCESS_DENIED_ERROR) 消息:拒绝用户'%s'@'%s'的访问(使用密码:%s) · 错误:1046 SQLSTATE: 3D000 (ER_NO_DB_ERROR) 消息:未选择数据库。 · 错误:1047 SQLSTATE: 08S01 (ER_UNKNOWN_COM_ERROR) 消息:未知命令。 · 错误:1048 SQLSTATE: 23000 (ER_BAD_NULL_ERROR) 消息:列'%s'不能为空。 · 错误:1049 SQLSTATE: 42000 (ER_BAD_DB_ERROR) 消息:未知数据库'%s'。 · 错误:1050 SQLSTATE: 42S01 (ER_TABLE_EXISTS_ERROR) 消息:表'%s'已存在。 · 错误:1051 SQLSTATE: 42S02 (ER_BAD_TABLE_ERROR) 消息:未知表'%s'。 · 错误:1052 SQLSTATE: 23000 (ER_NON_UNIQ_ERROR) 消息:%s中的列'%s'不明确。 · 错误:1053 SQLSTATE: 08S01 (ER_SERVER_SHUTDOWN) 消息:在操作过程中服务器关闭。 · 错误:1054 SQLSTATE: 42S22 (ER_BAD_FIELD_ERROR) 消息:'%s'中的未知列'%s'。 · 错误:1055 SQLSTATE: 42000 (ER_WRONG_FIELD_WITH_GROUP) 消息:'%s'不在GROUP BY中。 · 错误:1056 SQLSTATE: 42000 (ER_WRONG_GROUP_FIELD) 消息:无法在'%s'上创建组。 · 错误:1057 SQLSTATE: 42000 (ER_WRONG_SUM_SELECT) 消息:语句中有sum函数和相同语句中的列。 · 错误:1058 SQLSTATE: 21S01 (ER_WRONG_VALUE_COUNT) 消息:列计数不匹配值计数。 · 错误:1059 SQLSTATE: 42000 (ER_TOO_LONG_IDENT) 消息:ID名称'%s'过长。 · 错误:1060 SQLSTATE: 42S21 (ER_DUP_FIELDNAME) 消息:重复列名'%s'。 · 错误:1061 SQLSTATE: 42000 (ER_DUP_KEYNAME) 消息:重复键名称'%s'。 · 错误:1062 SQLSTATE: 23000 (ER_DUP_ENTRY) 消息:键%d的重复条目'%s'。 · 错误:1063 SQLSTATE: 42000 (ER_WRONG_FIELD_SPEC) 消息:对于列'%s',列分类符不正确。 · 错误:1064 SQLSTATE: 42000 (ER_PARSE_ERROR) 消息:在行%d上,%s靠近'%s'。 · 错误:1065 SQLSTATE: 42000 (ER_EMPTY_QUERY) 消息:查询为空。 · 错误:1066 SQLSTATE: 42000 (ER_NONUNIQ_TABLE) 消息:非唯一的表/别名:'%s' · 错误:1067 SQLSTATE: 42000 (ER_INVALID_DEFAULT) 消息:关于'%s'的无效默认值。 · 错误:1068 SQLSTATE: 42000 (ER_MULTIPLE_PRI_KEY) 消息:定义了多个主键。 · 错误:1069 SQLSTATE: 42000 (ER_TOO_MANY_KEYS) 消息:指定了过多键:允许的最大键数是%d。 · 错误:1070 SQLSTATE: 42000 (ER_TOO_MANY_KEY_PARTS) 消息:指定了过多键部分:允许的最大键部分是%d。 · 错误:1071 SQLSTATE: 42000 (ER_TOO_LONG_KEY) 消息:指定的键过长,最大键长度是%d字节。 · 错误:1072 SQLSTATE: 42000 (ER_KEY_COLUMN_DOES_NOT_EXITS) 消息:键列'%s'在表中不存在。 · 错误:1073 SQLSTATE: 42000 (ER_BLOB_USED_AS_KEY) 消息:BLOB列'%s'不能与已使用的表类型用在键说明中。 · 错误:1074 SQLSTATE: 42000 (ER_TOO_BIG_FIELDLENGTH) 消息:对于列'%s',列长度过大 (max = %d),请使用BLOB或TEXT取而代之。 · 错误:1075 SQLSTATE: 42000 (ER_WRONG_AUTO_KEY) 消息:不正确的表定义,只能有1个auto列,而且必须将其定义为键。 · 错误:1076 SQLSTATE: HY000 (ER_READY) 消息:%s,连接就绪。版本:'%s',套接字:'%s',端口:%d · 错误:1077 SQLSTATE: HY000 (ER_NORMAL_SHUTDOWN) 消息:%s,正常关闭。 · 错误:1078 SQLSTATE: HY000 (ER_GOT_SIGNAL) 消息:%s,获得信号%d。放弃! · 错误:1079 SQLSTATE: HY000 (ER_SHUTDOWN_COMPLETE) 消息:%s,关闭完成 · 错误:1080 SQLSTATE: 08S01 (ER_FORCING_CLOSE) 消息:%s,强制关闭线程%ld,用户:'%s' · 错误:1081 SQLSTATE: 08S01 (ER_IPSOCK_ERROR) 消息:无法创建IP套接字 · 错误:1082 SQLSTATE: 42S12 (ER_NO_SUCH_INDEX) 消息:表'%s'中没有与CREATE INDEX中索引类似的索引,重新创建表。 · 错误:1083 SQLSTATE: 42000 (ER_WRONG_FIELD_TERMINATORS) 消息:字段分隔符参量不是预期的,请参考手册。 · 错误:1084 SQLSTATE: 42000 (ER_BLOBS_AND_NO_TERMINATED) 消息:不能与BLOB一起使用固定行长度,请使用'fields terminated by'。 · 错误:1085 SQLSTATE: HY000 (ER_TEXTFILE_NOT_READABLE) 消息:文件'%s'必须在数据库目录下,或能被所有人读取。 · 错误:1086 SQLSTATE: HY000 (ER_FILE_EXISTS_ERROR) 消息:文件'%s'已存在。 · 错误:1087 SQLSTATE: HY000 (ER_LOAD_INFO) 消息:记录,%ld;已删除,%ld;已跳过,%ld;警告,%ld · 错误:1088 SQLSTATE: HY000 (ER_ALTER_INFO) 消息:记录,%ld;重复,%ld · 错误:1089 SQLSTATE: HY000 (ER_WRONG_SUB_KEY) 消息:不正确的子部分键,使用的键部分不是字符串,所用的长度长于键部分,或存储引擎不支持唯一子键。 · 错误:1090 SQLSTATE: 42000 (ER_CANT_REMOVE_ALL_FIELDS) 消息:不能用ALTER TABLE删除所有列,请使用DROP TABLE取而代之。 · 错误:1091 SQLSTATE: 42000 (ER_CANT_DROP_FIELD_OR_KEY) 消息:不能撤销'%s',请检查列/键是否存在。 · 错误:1092 SQLSTATE: HY000 (ER_INSERT_INFO) 消息:记录,%ld;复制,%ld;告警,%ld · 错误:1093 SQLSTATE: HY000 (ER_UPDATE_TABLE_USED) 消息:不能在FROM子句中制定要更新的目标表'%s'。 · 错误:1094 SQLSTATE: HY000 (ER_NO_SUCH_THREAD) 消息:未知线程ID:%lu · 错误:1095 SQLSTATE: HY000 (ER_KILL_DENIED_ERROR) 消息:你不是线程%lu的所有者。 · 错误:1096 SQLSTATE: HY000 (ER_NO_TABLES_USED) 消息:未使用任何表。 · 错误:1097 SQLSTATE: HY000 (ER_TOO_BIG_SET) 消息:列%s和SET的字符串过多。 · 错误:1098 SQLSTATE: HY000 (ER_NO_UNIQUE_LOGFILE) 消息:不能生成唯一的日志文件名%s.(1-999) · 错误:1099 SQLSTATE: HY000 (ER_TABLE_NOT_LOCKED_FOR_WRITE) 消息:表'%s'已用READ锁定,不能更新。 · 错误:1100 SQLSTATE: HY000 (ER_TABLE_NOT_LOCKED) 消息:未使用LOCK TABLES锁定表'%s'。 · 错误:1101 SQLSTATE: 42000 (ER_BLOB_CANT_HAVE_DEFAULT) 消息:BLOB/TEXT列'%s'不能有默认值。 · 错误:1102 SQLSTATE: 42000 (ER_WRONG_DB_NAME) 消息:不正确的数据库名'%s'。 · 错误:1103 SQLSTATE: 42000 (ER_WRONG_TABLE_NAME) 消息:不正确的表名'%s'。 · 错误:1104 SQLSTATE: 42000 (ER_TOO_BIG_SELECT) 消息:SELECT将检查超过MAX_JOIN_SIZE的行,如果SELECT正常,请检查WHERE,并使用SET SQL_BIG_SELECTS=1或SET SQL_MAX_JOIN_SIZE=#。 · 错误:1105 SQLSTATE: HY000 (ER_UNKNOWN_ERROR) 消息:未知错误。 · 错误:1106 SQLSTATE: 42000 (ER_UNKNOWN_PROCEDURE) 消息:未知过程'%s' · 错误:1107 SQLSTATE: 42000 (ER_WRONG_PARAMCOUNT_TO_PROCEDURE) 消息:对于过程'%s',参数计数不正确 · 错误:1108 SQLSTATE: HY000 (ER_WRONG_PARAMETERS_TO_PROCEDURE) 消息:对于过程'%s',参数不正确 · 错误:1109 SQLSTATE: 42S02 (ER_UNKNOWN_TABLE) 消息:%s中的未知表%s · 错误:1110 SQLSTATE: 42000 (ER_FIELD_SPECIFIED_TWICE) 消息:列'%s'被指定了两次。 · 错误:1111 SQLSTATE: HY000 (ER_INVALID_GROUP_FUNC_USE) 消息:无效的分组函数使用 · 错误:1112 SQLSTATE: 42000 (ER_UNSUPPORTED_EXTENSION) 消息:表'%s'使用了该MySQL版本中不存在的扩展。 · 错误:1113 SQLSTATE: 42000 (ER_TABLE_MUST_HAVE_COLUMNS) 消息:1个表至少要有1列。 · 错误:1114 SQLSTATE: HY000 (ER_RECORD_FILE_FULL) 消息:表'%s'已满。 · 错误:1115 SQLSTATE: 42000 (ER_UNKNOWN_CHARACTER_SET) 消息:未知字符集'%s'。 · 错误:1116 SQLSTATE: HY000 (ER_TOO_MANY_TABLES) 消息:表过多,MySQL在1个联合操作中只能使用%d个表。 · 错误:1117 SQLSTATE: HY000 (ER_TOO_MANY_FIELDS) 消息:列过多。 · 错误:1118 SQLSTATE: 42000 (ER_TOO_BIG_ROWSIZE) 消息:行的大小过大。对于所使用的表类型,不包括BLOB,最大行大小为%ld。必须将某些列更改 为TEXT或BLOB。 · 错误:1119 SQLSTATE: HY000 (ER_STACK_OVERRUN) 消息:线程堆栈溢出,已使用,%ld堆栈的%ld。如果需要,请使用'mysqld -O thread_stack=#'指定较大的堆 栈。 · 错误:1120 SQLSTATE: 42000 (ER_WRONG_OUTER_JOIN) 消息:在OUTER JOIN中发现交叉关联,请检查ON条件。 · 错误:1121 SQLSTATE: 42000 (ER_NULL_COLUMN_IN_INDEX) 消息:列'%s'与UNIQUE或INDEX一起使用,但未定义为NOT NULL。 · 错误:1122 SQLSTATE: HY000 (ER_CANT_FIND_UDF) 消息:无法加载函数'%s'。 · 错误:1123 SQLSTATE: HY000 (ER_CANT_INITIALIZE_UDF) 消息:无法初始化函数'%s'; %s · 错误:1124 SQLSTATE: HY000 (ER_UDF_NO_PATHS) 消息:对于共享库,不允许任何路径。 · 错误:1125 SQLSTATE: HY000 (ER_UDF_EXISTS) 消息:函数'%s'已存在。 · 错误:1126 SQLSTATE: HY000 (ER_CANT_OPEN_LIBRARY) 消息:不能打开共享库'%s' (errno: %d %s) · 错误:1127 SQLSTATE: HY000 (ER_CANT_FIND_DL_ENTRY) 消息:不能发现库中的符号'%s'。 · 错误:1128 SQLSTATE: HY000 (ER_FUNCTION_NOT_DEFINED) 消息:函数'%s'未定义。 · 错误:1129 SQLSTATE: HY000 (ER_HOST_IS_BLOCKED) 消息:由于存在很多连接错误,主机'%s'被屏蔽,请用'mysqladmin flush-hosts'解除屏蔽。 · 错误:1130 SQLSTATE: HY000 (ER_HOST_NOT_PRIVILEGED) 消息:不允许将主机'%s'连接到该MySQL服务器。 · 错误:1131 SQLSTATE: 42000 (ER_PASSWORD_ANONYMOUS_USER) 消息:你正在已匿名用户身份使用MySQL,不允许匿名用户更改密码。 · 错误:1132 SQLSTATE: 42000 (ER_PASSWORD_NOT_ALLOWED) 消息:必须有更新mysql数据库中表的权限才能更改密码。 · 错误:1133 SQLSTATE: 42000 (ER_PASSWORD_NO_MATCH) 消息:无法在用户表中找到匹配行。 · 错误:1134 SQLSTATE: HY000 (ER_UPDATE_INFO) 消息:行匹配,%ld;已更改,%ld;警告,%ld · 错误:1135 SQLSTATE: HY000 (ER_CANT_CREATE_THREAD) 消息:无法创建新线程(errno %d),如果未出现内存溢出,请参阅手册以了解可能的与操作系统有关的缺陷。 · 错误:1136 SQLSTATE: 21S01 (ER_WRONG_VALUE_COUNT_ON_ROW) 消息:列计数不匹配行%ld上的值计数。 · 错误:1137 SQLSTATE: HY000 (ER_CANT_REOPEN_TABLE) 消息:无法再次打开表'%s'。 · 错误:1138 SQLSTATE: 22004 (ER_INVALID_USE_OF_NULL) 消息:NULL值使用无效。 · 错误:1139 SQLSTATE: 42000 (ER_REGEXP_ERROR) 消息:获得来自regexp的错误'%s'。 · 错误:1140 SQLSTATE: 42000 (ER_MIX_OF_GROUP_FUNC_AND_FIELDS) 消息:如果没有GROUP BY子句,GROUP列 (MIN(),MAX(),COUNT(),...)与非GROUP列的混合不合法。 · 错误:1141 SQLSTATE: 42000 (ER_NONEXISTING_GRANT) 消息:没有为主机'%s'上的用户'%s'定义这类授权。 · 错误:1142 SQLSTATE: 42000 (ER_TABLEACCESS_DENIED_ERROR) 消息:拒绝用户'%s'@'%s'在表'%s'上使用%s命令。 · 错误:1143 SQLSTATE: 42000 (ER_COLUMNACCESS_DENIED_ERROR) 消息:拒绝用户'%s'@'%s'在表'%s'的'%s'上使用%s命令。 · 错误:1144 SQLSTATE: 42000 (ER_ILLEGAL_GRANT_FOR_TABLE) 消息:非法GRANT/REVOKE命令,请参阅手册以了解可使用那种权限。 · 错误:1145 SQLSTATE: 42000 (ER_GRANT_WRONG_HOST_OR_USER) 消息:GRANT的主机或用户参量过长。 · 错误:1146 SQLSTATE: 42S02 (ER_NO_SUCH_TABLE) 消息:表'%s.%s'不存在。 · 错误:1147 SQLSTATE: 42000 (ER_NONEXISTING_TABLE_GRANT) 消息:在表'%s'上没有为主机'%s'上的用户'%s'定义的这类授权。 · 错误:1148 SQLSTATE: 42000 (ER_NOT_ALLOWED_COMMAND) 消息:所使用的命令在该MySQL版本中不允许。 · 错误:1149 SQLSTATE: 42000 (ER_SYNTAX_ERROR) 消息:存在SQL语法错误,请参阅与你的MySQL版本对应的手册,以了解正确的语法。 · 错误:1150 SQLSTATE: HY000 (ER_DELAYED_CANT_CHANGE_LOCK) 消息:对于表%s,延迟的插入线程不能获得请求的锁定。 · 错误:1151 SQLSTATE: HY000 (ER_TOO_MANY_DELAYED_THREADS) 消息:使用的延迟线程过多。 · 错误:1152 SQLSTATE: 08S01 (ER_ABORTING_CONNECTION) 消息:与数据库'%s'和用户'%s'的连接%ld失败 (%s) · 错误:1153 SQLSTATE: 08S01 (ER_NET_PACKET_TOO_LARGE) 消息:获得信息包大于'max_allowed_packet'字节。 · 错误:1154 SQLSTATE: 08S01 (ER_NET_READ_ERROR_FROM_PIPE) 消息:获得来自连接管道的读错误。 · 错误:1155 SQLSTATE: 08S01 (ER_NET_FCNTL_ERROR) 消息:获得来自fcntl()的错误。 · 错误:1156 SQLSTATE: 08S01 (ER_NET_PACKETS_OUT_OF_ORDER) 消息:获得信息包无序。 · 错误:1157 SQLSTATE: 08S01 (ER_NET_UNCOMPRESS_ERROR) 消息:无法解压缩通信信息包。 · 错误:1158 SQLSTATE: 08S01 (ER_NET_READ_ERROR) 消息:读取通信信息包时出错。 · 错误:1159 SQLSTATE: 08S01 (ER_NET_READ_INTERRUPTED) 消息:读取通信信息包时出现超时。 · 错误:1160 SQLSTATE: 08S01 (ER_NET_ERROR_ON_WRITE) 消息:写入通信信息包时出错。 · 错误:1161 SQLSTATE: 08S01 (ER_NET_WRITE_INTERRUPTED) 消息:写入通信信息包时出现超时。 · 错误:1162 SQLSTATE: 42000 (ER_TOO_LONG_STRING) 消息:结果字符串长于'max_allowed_packet'字节。 · 错误:1163 SQLSTATE: 42000 (ER_TABLE_CANT_HANDLE_BLOB) 消息:所使用的表类型不支持BLOB/TEXT列。 · 错误:1164 SQLSTATE: 42000 (ER_TABLE_CANT_HANDLE_AUTO_INCREMENT) 消息:所使用的表类型不支持AUTO_INCREMENT列。 · 错误:1165 SQLSTATE: HY000 (ER_DELAYED_INSERT_TABLE_LOCKED) 消息:由于用LOCK TABLES锁定了表,INSERT DELAYED不能与表'%s'一起使用。 · 错误:1166 SQLSTATE: 42000 (ER_WRONG_COLUMN_NAME) 消息:不正确的列名'%s'。 · 错误:1167 SQLSTATE: 42000 (ER_WRONG_KEY_COLUMN) 消息:所使用的存储引擎不能为列'%s'编制索引。 · 错误:1168 SQLSTATE: HY000 (ER_WRONG_MRG_TABLE) 消息:MERGE表中的所有表未同等定义。 · 错误:1169 SQLSTATE: 23000 (ER_DUP_UNIQUE) 消息:由于唯一性限制,不能写入到表'%s'。 · 错误:1170 SQLSTATE: 42000 (ER_BLOB_KEY_WITHOUT_LENGTH) 消息:在未指定键长度的键说明中使用了BLOB/TEXT列'%s'。 · 错误:1171 SQLSTATE: 42000 (ER_PRIMARY_CANT_HAVE_NULL) 消息:PRIMARY KEY的所有部分必须是NOT NULL,如果需要为NULL的关键字,请使用UNIQUE取而代之。 · 错误:1172 SQLSTATE: 42000 (ER_TOO_MANY_ROWS) 消息:结果有1个以上的行组成。 · 错误:1173 SQLSTATE: 42000 (ER_REQUIRES_PRIMARY_KEY) 消息:该表类型要求主键。 · 错误:1174 SQLSTATE: HY000 (ER_NO_RAID_COMPILED) 消息:该MySQL版本是未使用RAID支持而编译的。 · 错误:1175 SQLSTATE: HY000 (ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE) 消息:你正在使用安全更新模式,而且试图在不使用WHERE的情况下更新使用了KEY列的表。 · 错误:1176 SQLSTATE: HY000 (ER_KEY_DOES_NOT_EXITS) 消息:在表'%s'中,键'%s'不存在。 · 错误:1177 SQLSTATE: 42000 (ER_CHECK_NO_SUCH_TABLE) 消息:无法打开表。 · 错误:1178 SQLSTATE: 42000 (ER_CHECK_NOT_IMPLEMENTED) 消息:用于表的引擎不支持%s。 · 错误:1179 SQLSTATE: 25000 (ER_CANT_DO_THIS_DURING_AN_TRANSACTION) 消息:不允许在事务中执行该命令。 · 错误:1180 SQLSTATE: HY000 (ER_ERROR_DURING_COMMIT) 消息:在COMMIT期间出现错误%d。 · 错误:1181 SQLSTATE: HY000 (ER_ERROR_DURING_ROLLBACK) 消息:在ROLLBACK期间出现错误%d。 · 错误:1182 SQLSTATE: HY000 (ER_ERROR_DURING_FLUSH_LOGS) 消息:在FLUSH_LOGS期间出现错误%d。 · 错误:1183 SQLSTATE: HY000 (ER_ERROR_DURING_CHECKPOINT) 消息:在CHECKPOINT期间出现错误%d。 · 错误:1184 SQLSTATE: 08S01 (ER_NEW_ABORTING_CONNECTION) 消息:与数据库'%s'、用户'%s'和主机'%s'的连接%ld失败 (%s)。 · 错误:1185 SQLSTATE: HY000 (ER_DUMP_NOT_IMPLEMENTED) 消息:针对表的存储引擎不支持二进制表转储。 · 错误:1186 SQLSTATE: HY000 (ER_FLUSH_MASTER_BINLOG_CLOSED) 消息:Binlog已关闭,不能RESET MASTER。 · 错误:1187 SQLSTATE: HY000 (ER_INDEX_REBUILD) 消息:重新创建转储表'%s'的索引失败。 · 错误:1188 SQLSTATE: HY000 (ER_MASTER) 消息:来自主连接'%s'的错误。 · 错误:1189 SQLSTATE: 08S01 (ER_MASTER_NET_READ) 消息:读取主连接时出现网络错误。 · 错误:1190 SQLSTATE: 08S01 (ER_MASTER_NET_WRITE) 消息:写入主连接时出现网络错误。 · 错误:1191 SQLSTATE: HY000 (ER_FT_MATCHING_KEY_NOT_FOUND) 消息:无法找到与列列表匹配的FULLTEXT索引。 · 错误:1192 SQLSTATE: HY000 (ER_LOCK_OR_ACTIVE_TRANSACTION) 消息:由于存在活动的锁定表或活动的事务,不能执行给定的命令。 · 错误:1193 SQLSTATE: HY000 (ER_UNKNOWN_SYSTEM_VARIABLE) 消息:未知的系统变量'%s'。 · 错误:1194 SQLSTATE: HY000 (ER_CRASHED_ON_USAGE) 消息:表'%s'被标记为崩溃,应予以修复。 · 错误:1195 SQLSTATE: HY000 (ER_CRASHED_ON_REPAIR) 消息:表'%s'被标记为崩溃,而且上次修复失败(自动?) · 错误:1196 SQLSTATE: HY000 (ER_WARNING_NOT_COMPLETE_ROLLBACK) 消息:不能回滚某些非事务性已变动表。 · 错误:1197 SQLSTATE: HY000 (ER_TRANS_CACHE_FULL) 消息:多语句事务要求更多的'max_binlog_cache_size'存储字节,增大mysqld变量,并再次尝试。 · 错误:1198 SQLSTATE: HY000 (ER_SLAVE_MUST_STOP) 消息:运行从实例时不能执行该操作,请首先运行STOP SLAVE。 · 错误:1199 SQLSTATE: HY000 (ER_SLAVE_NOT_RUNNING) 消息:该操作需要运行的从实例,请配置SLAVE并执行START SLAVE。 · 错误:1200 SQLSTATE: HY000 (ER_BAD_SLAVE) 消息:服务器未配置为从服务器,请更正config文件,或使用CHANGE MASTER TO。 · 错误:1201 SQLSTATE: HY000 (ER_MASTER_INFO) 消息:无法初始化主服务器信息结构,在MySQL错误日志中可找到更多错误消息。 · 错误:1202 SQLSTATE: HY000 (ER_SLAVE_THREAD) 消息:无法创建从线程,请检查系统资源。 · 错误:1203 SQLSTATE: 42000 (ER_TOO_MANY_USER_CONNECTIONS) 消息:用户%s已有了超过'max_user_connections'的活动连接。 · 错误:1204 SQLSTATE: HY000 (ER_SET_CONSTANTS_ONLY) 消息:或许仅应与SET一起使用常量表达式。 · 错误:1205 SQLSTATE: HY000 (ER_LOCK_WAIT_TIMEOUT) 消息:超过了锁定等待超时,请尝试重新启动事务。 · 错误:1206 SQLSTATE: HY000 (ER_LOCK_TABLE_FULL) 消息:总的锁定数超出了锁定表的大小。 · 错误:1207 SQLSTATE: 25000 (ER_READ_ONLY_TRANSACTION) 消息:在READ UNCOMMITTED事务期间,无法获得更新锁定。 · 错误:1208 SQLSTATE: HY000 (ER_DROP_DB_WITH_READ_LOCK) 消息:当线程保持为全局读锁定时,不允许DROP DATABASE。 · 错误:1209 SQLSTATE: HY000 (ER_CREATE_DB_WITH_READ_LOCK) 消息:当线程保持为全局读锁定时,不允许CREATE DATABASE。 · 错误:1210 SQLSTATE: HY000 (ER_WRONG_ARGUMENTS) 消息:为%s提供的参量不正确。 · 错误:1211 SQLSTATE: 42000 (ER_NO_PERMISSION_TO_CREATE_USER) 消息:不允许'%s'@'%s'创建新用户。 · 错误:1212 SQLSTATE: HY000 (ER_UNION_TABLES_IN_DIFFERENT_DIR) 消息:不正确的表定义,所有的MERGE表必须位于相同的数据库中。 · 错误:1213 SQLSTATE: 40001 (ER_LOCK_DEADLOCK) 消息:试图获取锁定时发现死锁,请尝试重新启动事务。 · 错误:1214 SQLSTATE: HY000 (ER_TABLE_CANT_HANDLE_FT) 消息:所使用的表类型不支持FULLTEXT索引。 · 错误:1215 SQLSTATE: HY000 (ER_CANNOT_ADD_FOREIGN) 消息:无法添加外键约束。 · 错误:1216 SQLSTATE: 23000 (ER_NO_REFERENCED_ROW) 消息:无法添加或更新子行,外键约束失败。 · 错误:1217 SQLSTATE: 23000 (ER_ROW_IS_REFERENCED) 消息:无法删除或更新父行,外键约束失败。 · 错误:1218 SQLSTATE: 08S01 (ER_CONNECT_TO_MASTER) 消息:连接至主服务器%s时出错。 · 错误:1219 SQLSTATE: HY000 (ER_QUERY_ON_MASTER) 消息:在主服务器%s上执行查询时出错。 · 错误:1220 SQLSTATE: HY000 (ER_ERROR_WHEN_EXECUTING_COMMAND) 消息:执行命令%s: %s时出错。 · 错误:1221 SQLSTATE: HY000 (ER_WRONG_USAGE) 消息:%s和%s的用法不正确。 · 错误:1222 SQLSTATE: 21000 (ER_WRONG_NUMBER_OF_COLUMNS_IN_SELECT) 消息:所使用的SELECT语句有不同的列数。 · 错误:1223 SQLSTATE: HY000 (ER_CANT_UPDATE_WITH_READLOCK) 消息:由于存在冲突的读锁定,无法执行查询。 · 错误:1224 SQLSTATE: HY000 (ER_MIXING_NOT_ALLOWED) 消息:禁止混合事务性表和非事务性表。 · 错误:1225 SQLSTATE: HY000 (ER_DUP_ARGUMENT) 消息:在语句中使用了两次选项'%s'。 · 错误:1226 SQLSTATE: 42000 (ER_USER_LIMIT_REACHED) 消息:用户'%s'超出了'%s'资源(当前值:%ld)。 · 错误:1227 SQLSTATE: 42000 (ER_SPECIFIC_ACCESS_DENIED_ERROR) 消息:拒绝访问,需要%s权限才能执行该操作。 · 错误:1228 SQLSTATE: HY000 (ER_LOCAL_VARIABLE) 消息:变量'%s'是1种SESSION变量,不能与SET GLOBAL一起使用。 · 错误:1229 SQLSTATE: HY000 (ER_GLOBAL_VARIABLE) 消息:变量'%s'是1种GLOBAL变量,应使用SET GLOBAL来设置它。 · 错误:1230 SQLSTATE: 42000 (ER_NO_DEFAULT) 消息:变量'%s'没有默认值。 · 错误:1231 SQLSTATE: 42000 (ER_WRONG_VALUE_FOR_VAR) 消息:变量'%s'不能设置为值'%s'。 · 错误:1232 SQLSTATE: 42000 (ER_WRONG_TYPE_FOR_VAR) 消息:变量'%s'的参量类型不正确。 · 错误:1233 SQLSTATE: HY000 (ER_VAR_CANT_BE_READ) 消息:变量'%s'只能被设置,不能被读取。 · 错误:1234 SQLSTATE: 42000 (ER_CANT_USE_OPTION_HERE) 消息:不正确的'%s'用法/位置。 · 错误:1235 SQLSTATE: 42000 (ER_NOT_SUPPORTED_YET) 消息:该MySQL版本尚不支持'%s'。 · 错误:1236 SQLSTATE: HY000 (ER_MASTER_FATAL_ERROR_READING_BINLOG) 消息:从二进制日志读取数据时,获得来自主服务器的致命错误%d: '%s'。 · 错误:1237 SQLSTATE: HY000 (ER_SLAVE_IGNORED_TABLE) 消息:由于“replicate-*-table”规则,从SQL线程忽略了查询。。 · 错误:1238 SQLSTATE: HY000 (ER_INCORRECT_GLOBAL_LOCAL_VAR) 消息:变量'%s'是一种%s变量。 · 错误:1239 SQLSTATE: 42000 (ER_WRONG_FK_DEF) 消息:对于 '%s': %s, 外键定义不正确。 · 错误:1240 SQLSTATE: HY000 (ER_KEY_REF_DO_NOT_MATCH_TABLE_REF) 消息:键引用和表引用不匹配。 · 错误:1241 SQLSTATE: 21000 (ER_OPERAND_COLUMNS) 消息:操作数应包含%d列。 · 错误:1242 SQLSTATE: 21000 (ER_SUBQUERY_NO_1_ROW) 消息:子查询返回1行以上。 · 错误:1243 SQLSTATE: HY000 (ER_UNKNOWN_STMT_HANDLER) 消息:指定给%s的未知预处理语句句柄。 · 错误:1244 SQLSTATE: HY000 (ER_CORRUPT_HELP_DB) 消息:帮助数据库崩溃或不存在。 · 错误:1245 SQLSTATE: HY000 (ER_CYCLIC_REFERENCE) 消息:对子查询的循环引用。 · 错误:1246 SQLSTATE: HY000 (ER_AUTO_CONVERT) 消息:将列'%s'从%s转换为%s。 · 错误:1247 SQLSTATE: 42S22 (ER_ILLEGAL_REFERENCE) 消息:引用'%s'不被支持 (%s)。 · 错误:1248 SQLSTATE: 42000 (ER_DERIVED_MUST_HAVE_ALIAS) 消息:所有的导出表必须有自己的别名。 · 错误:1249 SQLSTATE: 01000 (ER_SELECT_REDUCED) 消息:在优化期间简化了选择%u。 · 错误:1250 SQLSTATE: 42000 (ER_TABLENAME_NOT_ALLOWED_HERE) 消息:来自某一SELECT的表'%s'不能在%s中使用。 · 错误:1251 SQLSTATE: 08004 (ER_NOT_SUPPORTED_AUTH_MODE) 消息:客户端不支持服务器请求的鉴定协议,请考虑升级MySQL客户端。 · 错误:1252 SQLSTATE: 42000 (ER_SPATIAL_CANT_HAVE_NULL) 消息:SPATIAL索引的所有部分必须是NOT NULL。 · 错误:1253 SQLSTATE: 42000 (ER_COLLATION_CHARSET_MISMATCH) 消息:对于CHARACTER SET '%s',COLLATION '%s'无效。 · 错误:1254 SQLSTATE: HY000 (ER_SLAVE_WAS_RUNNING) 消息:从服务器正在运行。 · 错误:1255 SQLSTATE: HY000 (ER_SLAVE_WAS_NOT_RUNNING) 消息:从服务器已停止。 · 错误:1256 SQLSTATE: HY000 (ER_TOO_BIG_FOR_UNCOMPRESS) 消息:解压的数据过大,最大大小为%d(也可能是,解压数据的长度已损坏)。 · 错误:1257 SQLSTATE: HY000 (ER_ZLIB_Z_MEM_ERROR) 消息:ZLIB,无足够内存。 · 错误:1258 SQLSTATE: HY000 (ER_ZLIB_Z_BUF_ERROR) 消息:ZLIB,输出缓冲区内无足够空间(也可能是,解压数据的长度已损坏)。 · 错误:1259 SQLSTATE: HY000 (ER_ZLIB_Z_DATA_ERROR) 消息:ZLIB,输入数据已损坏。 · 错误:1260 SQLSTATE: HY000 (ER_CUT_VALUE_GROUP_CONCAT) 消息:%d行被GROUP_CONCAT()截去。 · 错误:1261 SQLSTATE: 01000 (ER_WARN_TOO_FEW_RECORDS) 消息:行%ld不包含所有列的数据。 · 错误:1262 SQLSTATE: 01000 (ER_WARN_TOO_MANY_RECORDS) 消息:行%ld被解短,它包含的数据大于输入列中的数据。 · 错误:1263 SQLSTATE: 22004 (ER_WARN_NULL_TO_NOTNULL) 消息:列被设为默认值,在行%ld上将NULL提供给了NOT NULL列。 · 错误:1264 SQLSTATE: 22003 (ER_WARN_DATA_OUT_OF_RANGE) 消息:为行%ld上的列'%s'调整超出范围的值。 · 错误:1265 SQLSTATE: 01000 (WARN_DATA_TRUNCATED) 消息:为行%ld上的列'%s'截短数据。 · 错误:1266 SQLSTATE: HY000 (ER_WARN_USING_OTHER_HANDLER) 消息:为表%s使用存储引擎%s。 · 错误:1267 SQLSTATE: HY000 (ER_CANT_AGGREGATE_2COLLATIONS) 消息:对于操作'%s',非法混合了校对(%s,%s)和(%s,%s)。 · 错误:1268 SQLSTATE: HY000 (ER_DROP_USER) 消息:无法撤销1个或多个请求的用户。 · 错误:1269 SQLSTATE: HY000 (ER_REVOKE_GRANTS) 消息:无法撤销所有权限,为1个或多个请求的用户授权。 · 错误:1270 SQLSTATE: HY000 (ER_CANT_AGGREGATE_3COLLATIONS) 消息:对于操作'%s',非法混合了校对(%s,%s)、(%s,%s)和(%s,%s)。 · 错误:1271 SQLSTATE: HY000 (ER_CANT_AGGREGATE_NCOLLATIONS) 消息:对于操作'%s',非法混合了校对。 · 错误:1272 SQLSTATE: HY000 (ER_VARIABLE_IS_NOT_STRUCT) 消息:变量'%s'不是变量组分(不能用作XXXX.variable_name)。 · 错误:1273 SQLSTATE: HY000 (ER_UNKNOWN_COLLATION) 消息:未知校对'%s'。 · 错误:1274 SQLSTATE: HY000 (ER_SLAVE_IGNORED_SSL_PARAMS) 消息:由于该MySQL从服务器是在不支持SSL的情况下编译的,CHANGE MASTER中的SSL参数被忽略,随后, 如果启动了具备SSL功能的MySQL,可使用这些参数。 · 错误:1275 SQLSTATE: HY000 (ER_SERVER_IS_IN_SECURE_AUTH_MODE) 消息:服务器正运行在“--secure-auth”模式下,但'%s'@'%s'有1个采用旧格式的密码,请将密码更改为新格 式。 · 错误:1276 SQLSTATE: HY000 (ER_WARN_FIELD_RESOLVED) 消息:SELECT #%d的字段或引用'%s%s%s%s%s'是在SELECT #%d中确定的。 · 错误:1277 SQLSTATE: HY000 (ER_BAD_SLAVE_UNTIL_COND) 消息:对于START SLAVE UNTIL,不正确的参数或参数组合。 · 错误:1278 SQLSTATE: HY000 (ER_MISSING_SKIP_SLAVE) 消息:与START SLAVE UNTIL一起执行按步复制时,建议使用“--skip-slave-start”,否则,如果发生未预料的 从服务器mysqld重启,间出现问题。 · 错误:1279 SQLSTATE: HY000 (ER_UNTIL_COND_IGNORED) 消息:SQL线程未启动,因而UNTIL选项被忽略。 · 错误:1280 SQLSTATE: 42000 (ER_WRONG_NAME_FOR_INDEX) 消息:不正确的索引名'%s'。 · 错误:1281 SQLSTATE: 42000 (ER_WRONG_NAME_FOR_CATALOG) 消息:不正确的目录名'%s'。 · 错误:1282 SQLSTATE: HY000 (ER_WARN_QC_RESIZE) 消息:查询高速缓冲设置大小%lu时失败,新的查询高速缓冲的大小是%lu。 · 错误:1283 SQLSTATE: HY000 (ER_BAD_FT_COLUMN) 消息:列'%s'不能是FULLTEXT索引的一部分。 · 错误:1284 SQLSTATE: HY000 (ER_UNKNOWN_KEY_CACHE) 消息:未知的键高速缓冲'%s'。 · 错误:1285 SQLSTATE: HY000 (ER_WARN_HOSTNAME_WONT_WORK) 消息:MySQL是在“--skip-name-resolve”模式下启动的,必须在不使用该开关的情况下重启它,以便该授权能 起作用。 · 错误:1286 SQLSTATE: 42000 (ER_UNKNOWN_STORAGE_ENGINE) 消息:未知的表引擎'%s'。 · 错误:1287 SQLSTATE: HY000 (ER_WARN_DEPRECATED_SYNTAX) 消息:'%s'已过时,请使用'%s'取而代之。 · 错误:1288 SQLSTATE: HY000 (ER_NON_UPDATABLE_TABLE) 消息:%s的目标表%s不可更新。 · 错误:1289 SQLSTATE: HY000 (ER_FEATURE_DISABLED) 消息:'%s'特性已被禁止,要想使其工作,需要用'%s'创建MySQL。 · 错误:1290 SQLSTATE: HY000 (ER_OPTION_PREVENTS_STATEMENT) 消息:MySQL正使用%s选项运行,因此不能执行该语句。 · 错误:1291 SQLSTATE: HY000 (ER_DUPLICATED_VALUE_IN_TYPE) 消息:列'%s'在%s中有重复值'%s'。 · 错误:1292 SQLSTATE: 22007 (ER_TRUNCATED_WRONG_VALUE) 消息:截短了不正确的%s值: '%s' · 错误:1293 SQLSTATE: HY000 (ER_TOO_MUCH_AUTO_TIMESTAMP_COLS) 消息:不正确的表定义,在DEFAULT或ON UPDATE子句中,对于CURRENT_TIMESTAMP,只能有一 个TIMESTAMP列。 · 错误:1294 SQLSTATE: HY000 (ER_INVALID_ON_UPDATE) 消息:对于'%s'列,ON UPDATE子句无效。 · 错误:1295 SQLSTATE: HY000 (ER_UNSUPPORTED_PS) 消息:在预处理语句协议中,尚不支持该命令。 · 错误:1296 SQLSTATE: HY000 (ER_GET_ERRMSG) 消息:从%s获得错误%d '%s'。 · 错误:1297 SQLSTATE: HY000 (ER_GET_TEMPORARY_ERRMSG) 消息:从%s获得临时错误%d '%s'。 · 错误:1298 SQLSTATE: HY000 (ER_UNKNOWN_TIME_ZONE) 消息:未知或不正确的时区: '%s' · 错误:1299 SQLSTATE: HY000 (ER_WARN_INVALID_TIMESTAMP) 消息:在行%ld的列'%s'中存在无效的TIMESTAMP值。 · 错误:1300 SQLSTATE: HY000 (ER_INVALID_CHARACTER_STRING) 消息:无效的%s字符串: '%s' · 错误:1301 SQLSTATE: HY000 (ER_WARN_ALLOWED_PACKET_OVERFLOWED) 消息:%s()的结果大于max_allowed_packet (%ld),已截短 · 错误:1302 SQLSTATE: HY000 (ER_CONFLICTING_DECLARATIONS) 消息:冲突声明:'%s%s'和'%s%s' · 错误:1303 SQLSTATE: 2F003 (ER_SP_NO_RECURSIVE_CREATE) 消息:不能从另一个存储子程序中创建%s。 · 错误:1304 SQLSTATE: 42000 (ER_SP_ALREADY_EXISTS) 消息:%s %s已存在。 · 错误:1305 SQLSTATE: 42000 (ER_SP_DOES_NOT_EXIST) 消息:%s %s不存在。 · 错误:1306 SQLSTATE: HY000 (ER_SP_DROP_FAILED) 消息:DROP %s %s失败 · 错误:1307 SQLSTATE: HY000 (ER_SP_STORE_FAILED) 消息:CREATE %s %s失败。 · 错误:1308 SQLSTATE: 42000 (ER_SP_LILABEL_MISMATCH) 消息:%s无匹配标签: %s · 错误:1309 SQLSTATE: 42000 (ER_SP_LABEL_REDEFINE) 消息:重新定义标签%s · 错误:1310 SQLSTATE: 42000 (ER_SP_LABEL_MISMATCH) 消息:末端标签%s无匹配项 · 错误:1311 SQLSTATE: 01000 (ER_SP_UNINIT_VAR) 消息:正在引用未初始化的变量%s。 · 错误:1312 SQLSTATE: 0A000 (ER_SP_BADSELECT) 消息:PROCEDURE %s不能在给定场景下返回结果集。 · 错误:1313 SQLSTATE: 42000 (ER_SP_BADRETURN) 消息:仅在FUNCTION中允许RETURN。 · 错误:1314 SQLSTATE: 0A000 (ER_SP_BADSTATEMENT) 消息:在存储程序中不允许%s。 · 错误:1315 SQLSTATE: 42000 (ER_UPDATE_LOG_DEPRECATED_IGNORED) 消息:更新日志已被放弃,并用二进制日志取代,SET SQL_LOG_UPDATE被忽略。 · 错误:1316 SQLSTATE: 42000 (ER_UPDATE_LOG_DEPRECATED_TRANSLATED) 消息:更新日志已被放弃,并用二进制日志取代,SET SQL_LOG_UPDATE已被截短为SET SQL_LOG_BIN。 · 错误:1317 SQLSTATE: 70100 (ER_QUERY_INTERRUPTED) 消息:查询执行被中断。 · 错误:1318 SQLSTATE: 42000 (ER_SP_WRONG_NO_OF_ARGS) 消息:对于%s %s,参量数目不正确,预期为%u,但却是%u。 · 错误:1319 SQLSTATE: 42000 (ER_SP_COND_MISMATCH) 消息:未定义的CONDITION: %s · 错误:1320 SQLSTATE: 42000 (ER_SP_NORETURN) 消息:在FUNCTION %s中未发现RETURN。 · 错误:1321 SQLSTATE: 2F005 (ER_SP_NORETURNEND) 消息:FUNCTION %s结束时缺少RETURN。 · 错误:1322 SQLSTATE: 42000 (ER_SP_BAD_CURSOR_QUERY) 消息:光标语句必须是SELECT。 · 错误:1323 SQLSTATE: 42000 (ER_SP_BAD_CURSOR_SELECT) 消息:光标SELECT不得有INTO。 · 错误:1324 SQLSTATE: 42000 (ER_SP_CURSOR_MISMATCH) 消息:未定义的CURSOR: %s · 错误:1325 SQLSTATE: 24000 (ER_SP_CURSOR_ALREADY_OPEN) 消息:光标已打开 · 错误:1326 SQLSTATE: 24000 (ER_SP_CURSOR_NOT_OPEN) 消息:光标未打开 · 错误:1327 SQLSTATE: 42000 (ER_SP_UNDECLARED_VAR) 消息:未声明的变量:%s · 错误:1328 SQLSTATE: HY000 (ER_SP_WRONG_NO_OF_FETCH_ARGS) 消息:不正确的FETCH变量数目。 · 错误:1329 SQLSTATE: 02000 (ER_SP_FETCH_NO_DATA) 消息:FETCH无数据。 · 错误:1330 SQLSTATE: 42000 (ER_SP_DUP_PARAM) 消息:重复参数: %s · 错误:1331 SQLSTATE: 42000 (ER_SP_DUP_VAR) 消息:重复变量: %s · 错误:1332 SQLSTATE: 42000 (ER_SP_DUP_COND) 消息:重复条件: %s · 错误:1333 SQLSTATE: 42000 (ER_SP_DUP_CURS) 消息:重复光标: %s · 错误:1334 SQLSTATE: HY000 (ER_SP_CANT_ALTER) 消息:ALTER %s %s失败。 · 错误:1335 SQLSTATE: 0A000 (ER_SP_SUBSELECT_NYI) 消息:不支持Subselect值。 · 错误:1336 SQLSTATE: 0A000 (ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG) 消息:在存储函数或触发程序中,不允许%s。 · 错误:1337 SQLSTATE: 42000 (ER_SP_VARCOND_AFTER_CURSHNDLR) 消息:光标或句柄声明后面的变量或条件声明。 · 错误:1338 SQLSTATE: 42000 (ER_SP_CURSOR_AFTER_HANDLER) 消息:句柄声明后面的光标声明。 · 错误:1339 SQLSTATE: 20000 (ER_SP_CASE_NOT_FOUND) 消息:对于CASE语句,未发现Case。 · 错误:1340 SQLSTATE: HY000 (ER_FPARSER_TOO_BIG_FILE) 消息:配置文件'%s'过大。 · 错误:1341 SQLSTATE: HY000 (ER_FPARSER_BAD_HEADER) 消息:文件'%s'中存在残缺的文件类型标题。 · 错误:1342 SQLSTATE: HY000 (ER_FPARSER_EOF_IN_COMMENT) 消息:解析'%s'时,文件意外结束。 · 错误:1343 SQLSTATE: HY000 (ER_FPARSER_ERROR_IN_PARAMETER) 消息:解析参数'%s'时出错(行:'%s')。 · 错误:1344 SQLSTATE: HY000 (ER_FPARSER_EOF_IN_UNKNOWN_PARAMETER) 消息:跳过未知参数'%s'时,文件意外结束。 · 错误:1345 SQLSTATE: HY000 (ER_VIEW_NO_EXPLAIN) 消息:EXPLAIN/SHOW无法发出,缺少对基本表的权限。 · 错误:1346 SQLSTATE: HY000 (ER_FRM_UNKNOWN_TYPE) 消息:文件'%s'在其题头中有未知的类型'%s'。 · 错误:1347 SQLSTATE: HY000 (ER_WRONG_OBJECT) 消息:'%s.%s'不是%s。 · 错误:1348 SQLSTATE: HY000 (ER_NONUPDATEABLE_COLUMN) 消息:列'%s'不可更新。 · 错误:1349 SQLSTATE: HY000 (ER_VIEW_SELECT_DERIVED) 消息:视图的SELECT在FROM子句中包含子查询。 · 错误:1350 SQLSTATE: HY000 (ER_VIEW_SELECT_CLAUSE) 消息:视图的SELECT包含'%s'子句。 · 错误:1351 SQLSTATE: HY000 (ER_VIEW_SELECT_VARIABLE) 消息:视图的SELECT包含1个变量或参数。 · 错误:1352 SQLSTATE: HY000 (ER_VIEW_SELECT_TMPTABLE) 消息:视图的SELECT引用了临时表'%s'。 · 错误:1353 SQLSTATE: HY000 (ER_VIEW_WRONG_LIST) 消息:视图的SELECT和视图的字段列表有不同的列计数。 · 错误:1354 SQLSTATE: HY000 (ER_WARN_VIEW_MERGE) 消息:此时,不能在这里使用视图合并算法(假定未定义算法)。 · 错误:1355 SQLSTATE: HY000 (ER_WARN_VIEW_WITHOUT_KEY) 消息:正在更新的视图没有其基本表的完整键。 · 错误:1356 SQLSTATE: HY000 (ER_VIEW_INVALID) 消息:视图'%s.%s'引用了无效的表、列、或函数,或视图的定义程序/调用程序缺少使用它们的权限。 · 错误:1357 SQLSTATE: HY000 (ER_SP_NO_DROP_SP) 消息:无法从另一个存储子程序中撤销或更改%s。 · 错误:1358 SQLSTATE: HY000 (ER_SP_GOTO_IN_HNDLR) 消息:在存储子程序句柄中不允许GOTO。 · 错误:1359 SQLSTATE: HY000 (ER_TRG_ALREADY_EXISTS) 消息:触发程序已存在。 · 错误:1360 SQLSTATE: HY000 (ER_TRG_DOES_NOT_EXIST) 消息:触发程序不存在。 · 错误:1361 SQLSTATE: HY000 (ER_TRG_ON_VIEW_OR_TEMP_TABLE) 消息:触发程序的'%s'是视图或临时表。 · 错误:1362 SQLSTATE: HY000 (ER_TRG_CANT_CHANGE_ROW) 消息:在%strigger中,不允许更新%s行。 · 错误:1363 SQLSTATE: HY000 (ER_TRG_NO_SUCH_ROW_IN_TRG) 消息:在%s触发程序中没有%s行。 · 错误:1364 SQLSTATE: HY000 (ER_NO_DEFAULT_FOR_FIELD) 消息:字段'%s'没有默认值。 · 错误:1365 SQLSTATE: 22012 (ER_DIVISION_BY_ZERO) 消息:被0除。 · 错误:1366 SQLSTATE: HY000 (ER_TRUNCATED_WRONG_VALUE_FOR_FIELD) 消息:不正确的%s值,'%s',对于行%ld 上的列'%s'。 · 错误:1367 SQLSTATE: 22007 (ER_ILLEGAL_VALUE_FOR_TYPE) 消息:解析过程中发现非法%s '%s'值。 · 错误:1368 SQLSTATE: HY000 (ER_VIEW_NONUPD_CHECK) 消息:不可更新视图'%s.%s'上的CHECK OPTION。 · 错误:1369 SQLSTATE: HY000 (ER_VIEW_CHECK_FAILED) 消息:CHECK OPTION失败,'%s.%s' · 错误:1370 SQLSTATE: 42000 (ER_PROCACCESS_DENIED_ERROR) 消息:对于子程序'%s',拒绝用户'%s'@'%s'使用%s命令。 · 错误:1371 SQLSTATE: HY000 (ER_RELAY_LOG_FAIL) 消息:清除旧中继日志失败,%s · 错误:1372 SQLSTATE: HY000 (ER_PASSWD_LENGTH) 消息:密码混编应是%d位的十六进制数。 · 错误:1373 SQLSTATE: HY000 (ER_UNKNOWN_TARGET_BINLOG) 消息:在binlog索引中未发现目标日志。 · 错误:1374 SQLSTATE: HY000 (ER_IO_ERR_LOG_INDEX_READ) 消息:读取日志索引文件时出现I/O错误。 · 错误:1375 SQLSTATE: HY000 (ER_BINLOG_PURGE_PROHIBITED) 消息:服务器配置不允许binlog清除。 · 错误:1376 SQLSTATE: HY000 (ER_FSEEK_FAIL) 消息:fseek()失败。 · 错误:1377 SQLSTATE: HY000 (ER_BINLOG_PURGE_FATAL_ERR) 消息:在日志清除过程中出现致命错误。 · 错误:1378 SQLSTATE: HY000 (ER_LOG_IN_USE) 消息:可清除的日志正在使用,不能清除。 · 错误:1379 SQLSTATE: HY000 (ER_LOG_PURGE_UNKNOWN_ERR) 消息:在日志清除过程中出现未知错误。 · 错误:1380 SQLSTATE: HY000 (ER_RELAY_LOG_INIT) 消息:初始化中继日志位置失败,%s · 错误:1381 SQLSTATE: HY000 (ER_NO_BINARY_LOGGING) 消息:未使用二进制日志功能。 · 错误:1382 SQLSTATE: HY000 (ER_RESERVED_SYNTAX) 消息:'%s'语法保留给MySQL服务器内部使用。 · 错误:1383 SQLSTATE: HY000 (ER_WSAS_FAILED) 消息:WSAStartup失败。 · 错误:1384 SQLSTATE: HY000 (ER_DIFF_GROUPS_PROC) 消息:尚不能用不同的组处理过程。 · 错误:1385 SQLSTATE: HY000 (ER_NO_GROUP_FOR_PROC) 消息:对于该过程,SELECT必须有1个组。 · 错误:1386 SQLSTATE: HY000 (ER_ORDER_WITH_PROC) 消息:不能与该过程一起使用ORDER子句。 · 错误:1387 SQLSTATE: HY000 (ER_LOGGING_PROHIBIT_CHANGING_OF) 消息:二进制日志功能和复制功能禁止更改全局服务器%s。 · 错误:1388 SQLSTATE: HY000 (ER_NO_FILE_MAPPING) 消息:无法映射文件: %s, errno: %d · 错误:1389 SQLSTATE: HY000 (ER_WRONG_MAGIC) 消息:%s中有错 · 错误:1390 SQLSTATE: HY000 (ER_PS_MANY_PARAM) 消息:预处理语句包含过多的占位符。 · 错误:1391 SQLSTATE: HY000 (ER_KEY_PART_0) 消息:键部分'%s'的长度不能为0。 · 错误:1392 SQLSTATE: HY000 (ER_VIEW_CHECKSUM) 消息:视图文本校验和失败。 · 错误:1393 SQLSTATE: HY000 (ER_VIEW_MULTIUPDATE) 消息:无法通过联合视图'%s.%s'更改1个以上的基本表。 · 错误:1394 SQLSTATE: HY000 (ER_VIEW_NO_INSERT_FIELD_LIST) 消息:不能在没有字段列表的情况下插入联合视图'%s.%s'。 · 错误:1395 SQLSTATE: HY000 (ER_VIEW_DELETE_MERGE_VIEW) 消息:不能从联合视图'%s.%s'中删除。 · 错误:1396 SQLSTATE: HY000 (ER_CANNOT_USER) 消息:对于%s的操作%s失败。 · 错误:1397 SQLSTATE: XAE04 (ER_XAER_NOTA) 消息:XAER_NOTA: 未知XID · 错误:1398 SQLSTATE: XAE05 (ER_XAER_INVAL) 消息:XAER_INVAL: 无效参量(或不支持的命令) · 错误:1399 SQLSTATE: XAE07 (ER_XAER_RMFAIL) 消息:XAER_RMFAIL: 当全局事务处于%s状态时,不能执行命令。 · 错误:1400 SQLSTATE: XAE09 (ER_XAER_OUTSIDE) 消息:XAER_OUTSIDE: 某些工作是在全局事务外完成的。 · 错误:1401 SQLSTATE: XAE03 (ER_XAER_RMERR) 消息:XAER_RMERR: 在事务分支中出现致命错误,请检查数据一致性。 · 错误:1402 SQLSTATE: XA100 (ER_XA_RBROLLBACK) 消息:XA_RBROLLBACK: 回滚了事务分支。 · 错误:1403 SQLSTATE: 42000 (ER_NONEXISTING_PROC_GRANT) 消息:在子程序'%s'上没有为主机'%s'上的用户'%s'定义的这类授权。 · 错误:1404 SQLSTATE: HY000 (ER_PROC_AUTO_GRANT_FAIL) 消息:无法授予EXECUTE和ALTER ROUTINE权限。 · 错误:1405 SQLSTATE: HY000 (ER_PROC_AUTO_REVOKE_FAIL) 消息:无法撤销已放弃子程序上的所有权限。 · 错误:1406 SQLSTATE: 22001 (ER_DATA_TOO_LONG) 消息:对于行%ld上的列'%s'来说,数据过长。 · 错误:1407 SQLSTATE: 42000 (ER_SP_BAD_SQLSTATE) 消息:不良SQLSTATE: '%s' · 错误:1408 SQLSTATE: HY000 (ER_STARTUP) 消息:%s,连接就绪;版本,'%s';套接字,'%s';端口,%d %s · 错误:1409 SQLSTATE: HY000 (ER_LOAD_FROM_FIXED_SIZE_ROWS_TO_VAR) 消息:不能从具有固定大小行的文件中将值加载到变量。 · 错误:1410 SQLSTATE: 42000 (ER_CANT_CREATE_USER_WITH_GRANT) 消息:不允许用GRANT创建用户。 · 错误:1411 SQLSTATE: HY000 (ER_WRONG_VALUE_FOR_TYPE) 消息:不正确的%s值,'%s',对于函数%s · 错误:1412 SQLSTATE: HY000 (ER_TABLE_DEF_CHANGED) 消息:表定义已更改,请再次尝试事务。 · 错误:1413 SQLSTATE: 42000 (ER_SP_DUP_HANDLER) 消息:在相同块中声明了重复句柄。 · 错误:1414 SQLSTATE: 42000 (ER_SP_NOT_VAR_ARG) 消息:子程序%s的OUT或INOUT参量不是变量。 · 错误:1415 SQLSTATE: 0A000 (ER_SP_NO_RETSET) 消息:不允许从%s返回结果集。 · 错误:1416 SQLSTATE: 22003 (ER_CANT_CREATE_GEOMETRY_OBJECT) 消息:不能从发送给GEOMETRY字段的数据中获取几何对象。 · 错误:1417 SQLSTATE: HY000 (ER_FAILED_ROUTINE_BREAK_BINLOG) 消息:1个子程序失败,在其声明没有NO SQL或READS SQL DATA,而且二进制日志功能已启用,如果更新了 非事务性表,二进制日志将丢失其变化信息。 · 错误:1418 SQLSTATE: HY000 (ER_BINLOG_UNSAFE_ROUTINE) 消息:在该子程序的在其声明没有DETERMINISTIC、NO SQL或READS SQL DATA,而且二进制日志功能已启 用(你或许打算使用不太安全的log_bin_trust_routine_creators变量)。 · 错误:1419 SQLSTATE: HY000 (ER_BINLOG_CREATE_ROUTINE_NEED_SUPER) 消息:你没有SUPER权限,而且二进制日志功能已启用(你或许打算使用不太安全 的log_bin_trust_routine_creators变量)。 · 错误:1420 SQLSTATE: HY000 (ER_EXEC_STMT_WITH_OPEN_CURSOR) 消息:不能执行该预处理语句,该预处理语句有与之相关的打开光标。请复位语句并再次执行。 · 错误:1421 SQLSTATE: HY000 (ER_STMT_HAS_NO_OPEN_CURSOR) 消息:语句(%lu)没有打开的光标。 · 错误:1422 SQLSTATE: HY000 (ER_COMMIT_NOT_ALLOWED_IN_SF_OR_TRG) 消息:在存储函数或触发程序中,不允许显式或隐式提交。 · 错误:1423 SQLSTATE: HY000 (ER_NO_DEFAULT_FOR_VIEW_FIELD) 消息:视图'%s.%s'基本表的字段没有默认值。 · 错误:1424 SQLSTATE: HY000 (ER_SP_NO_RECURSION) 消息:不允许递归存储子程序。 · 错误:1425 SQLSTATE: 42000 (ER_TOO_BIG_SCALE) 消息:为列'%s'指定了过大的标度%d。最大为%d。 · 错误:1426 SQLSTATE: 42000 (ER_TOO_BIG_PRECISION) 消息:为列'%s'指定了过高的精度%d。最大为%d。 · 错误:1427 SQLSTATE: 42000 (ER_M_BIGGER_THAN_D) 消息:对于float(M,D)、double(M,D)或decimal(M,D),M必须>= D (列'%s')。 · 错误:1428 SQLSTATE: HY000 (ER_WRONG_LOCK_OF_SYSTEM_TABLE) 消息:不能将系统'%s.%s'表的写锁定与其他表结合起来。 · 错误:1429 SQLSTATE: HY000 (ER_CONNECT_TO_FOREIGN_DATA_SOURCE) 消息:无法连接到外部数据源,数据库'%s'! · 错误:1430 SQLSTATE: HY000 (ER_QUERY_ON_FOREIGN_DATA_SOURCE) 消息:处理作用在外部数据源上的查询时出现问题。数据源错误:'%s' · 错误:1431 SQLSTATE: HY000 (ER_FOREIGN_DATA_SOURCE_DOESNT_EXIST) 消息:你试图引用的外部数据源不存在。数据源错误:'%s' · 错误:1432 SQLSTATE: HY000 (ER_FOREIGN_DATA_STRING_INVALID_CANT_CREATE) 消息:无法创建联合表。数据源连接字符串'%s'格式不正确。 · 错误:1433 SQLSTATE: HY000 (ER_FOREIGN_DATA_STRING_INVALID) 消息:数据源连接字符串'%s'格式不正确。 · 错误:1434 SQLSTATE: HY000 (ER_CANT_CREATE_FEDERATED_TABLE) 消息:无法创建联合表。外部数据源错误:'%s' · 错误:1435 SQLSTATE: HY000 (ER_TRG_IN_WRONG_SCHEMA) 消息:触发程序位于错误的方案中。 · 错误:1436 SQLSTATE: HY000 (ER_STACK_OVERRUN_NEED_MORE) 消息:线程堆栈溢出,%ld字节堆栈用了%ld字节,并需要%ld字节。请使用'mysqld -O thread_stack=#'指定 更大的堆栈。 · 错误:1437 SQLSTATE: 42000 (ER_TOO_LONG_BODY) 消息:'%s'的子程序主体过长。 · 错误:1438 SQLSTATE: HY000 (ER_WARN_CANT_DROP_DEFAULT_KEYCACHE) 消息:无法撤销默认的keycache。 · 错误:1439 SQLSTATE: 42000 (ER_TOO_BIG_DISPLAYWIDTH) 消息:对于列'%s',显示宽度超出范围(max = %d) · 错误:1440 SQLSTATE: XAE08 (ER_XAER_DUPID) 消息:XAER_DUPID: XID已存在 · 错误:1441 SQLSTATE: 22008 (ER_DATETIME_FUNCTION_OVERFLOW) 消息:日期时间函数,%s字段溢出。 · 错误:1442 SQLSTATE: HY000 (ER_CANT_UPDATE_USED_TABLE_IN_SF_OR_TRG) 消息:由于它已被调用了该存储函数/触发程序的语句使用,不能在存储函数/触发程序中更新表'%s'。 · 错误:1443 SQLSTATE: HY000 (ER_VIEW_PREVENT_UPDATE) 消息:表'%s'的定义不允许在表'%s上执行操作%s。 · 错误:1444 SQLSTATE: HY000 (ER_PS_NO_RECURSION) 消息:预处理语句包含引用了相同语句的存储子程序调用。不允许以这类递归方式执行预处理语句。 · 错误:1445 SQLSTATE: HY000 (ER_SP_CANT_SET_AUTOCOMMIT) 消息:不允许从存储函数或触发程序设置autocommit。 · 错误:1446 SQLSTATE: HY000 (ER_NO_VIEW_USER) 消息:视图定义人不完全合格。 · 错误:1447 SQLSTATE: HY000 (ER_VIEW_FRM_NO_USER) 消息:视图%s.%s没有定义人信息(旧的表格式)。当前用户将被当作定义人。请重新创建视图! · 错误:1448 SQLSTATE: HY000 (ER_VIEW_OTHER_USER) 消息:需要SUPER权限才能创建具有%s@%s定义器的视图。 · 错误:1449 SQLSTATE: HY000 (ER_NO_SUCH_USER) 消息:没有注册的%s@%s。 · 错误:1450 SQLSTATE: HY000 (ER_FORBID_SCHEMA_CHANGE) 消息:不允许将方案从'%s'变为'%s'。 · 错误:1451 SQLSTATE: 23000 (ER_ROW_IS_REFERENCED_2) 消息:不能删除或更新父行,外键约束失败(%s)。 · 错误:1452 SQLSTATE: 23000 (ER_NO_REFERENCED_ROW_2) 消息:不能添加或更新子行,外键约束失败(%s)。 · 错误:1453 SQLSTATE: 42000 (ER_SP_BAD_VAR_SHADOW) 消息:必须用`...`引用变量,或重新命名变量。 · 错误:1454 SQLSTATE: HY000 (ER_PARTITION_REQUIRES_VALUES_ERROR) 消息:对于每个分区,%s PARTITIONING需要VALUES %s的定义。 · 错误:1455 SQLSTATE: HY000 (ER_PARTITION_WRONG_VALUES_ERROR) 消息:在分区定义中,只有%s PARTITIONING能使用VALUES %s。 · 错误:1456 SQLSTATE: HY000 (ER_PARTITION_MAXVALUE_ERROR) 消息:MAXVALUE只能在最后1个分区定义中使用。 · 错误:1457 SQLSTATE: HY000 (ER_PARTITION_SUBPARTITION_ERROR) 消息:子分区只能是哈希分区,并按键分区。 · 错误:1458 SQLSTATE: HY000 (ER_PARTITION_WRONG_NO_PART_ERROR) 消息:定义了错误的分区数,与前面的设置不匹配。 · 错误:1459 SQLSTATE: HY000 (ER_PARTITION_WRONG_NO_SUBPART_ERROR) 消息:定义了错误的子分区数,与前面的设置不匹配。 · 错误:1460 SQLSTATE: HY000 (ER_CONST_EXPR_IN_PARTITION_FUNC_ERROR) 消息:在分区(子分区)函数中不允许使用常量/随机表达式。 · 错误:1461 SQLSTATE: HY000 (ER_NO_CONST_EXPR_IN_RANGE_OR_LIST_ERROR) 消息:RANGE/LIST VALUES中的表达式必须是常量。 · 错误:1462 SQLSTATE: HY000 (ER_FIELD_NOT_FOUND_PART_ERROR) 消息:在表中未发现分区函数字段列表中的字段。 · 错误:1463 SQLSTATE: HY000 (ER_LIST_OF_FIELDS_ONLY_IN_HASH_ERROR) 消息:仅在KEY分区中允许使用字段列表。 · 错误:1464 SQLSTATE: HY000 (ER_INCONSISTENT_PARTITION_INFO_ERROR) 消息:frm文件中的分区信息与能够写入到frm文件中的不一致。 · 错误:1465 SQLSTATE: HY000 (ER_PARTITION_FUNC_NOT_ALLOWED_ERROR) 消息:%s函数返回了错误类型。 · 错误:1466 SQLSTATE: HY000 (ER_PARTITIONS_MUST_BE_DEFINED_ERROR) 消息:对于%s分区,必须定义每个分区。 · 错误:1467 SQLSTATE: HY000 (ER_RANGE_NOT_INCREASING_ERROR) 消息:对于各分区,VALUES LESS THAN值必须严格增大。 · 错误:1468 SQLSTATE: HY000 (ER_INCONSISTENT_TYPE_OF_FUNCTIONS_ERROR) 消息:VALUES值必须与分区函数具有相同的类型。 · 错误:1469 SQLSTATE: HY000 (ER_MULTIPLE_DEF_CONST_IN_LIST_PART_ERROR) 消息:Multiple definition of same constant in list partitioning · 错误:1470 SQLSTATE: HY000 (ER_PARTITION_ENTRY_ERROR) 消息:在查询中,不能独立使用分区功能。 · 错误:1471 SQLSTATE: HY000 (ER_MIX_HANDLER_ERROR) 消息:在该MySQL版本中,不允许分区中的句柄组合。 · 错误:1472 SQLSTATE: HY000 (ER_PARTITION_NOT_DEFINED_ERROR) 消息:对于分区引擎,有必要定义所有的%s。 · 错误:1473 SQLSTATE: HY000 (ER_TOO_MANY_PARTITIONS_ERROR) 消息:定义了过多分区。 · 错误:1474 SQLSTATE: HY000 (ER_SUBPARTITION_ERROR) 消息:对于子分区,仅能将RANGE/LIST分区与HASH/KEY分区混合起来。 · 错误:1475 SQLSTATE: HY000 (ER_CANT_CREATE_HANDLER_FILE) 消息:无法创建特定的句柄文件。 · 错误:1476 SQLSTATE: HY000 (ER_BLOB_FIELD_IN_PART_FUNC_ERROR) 消息:在分区函数中,不允许使用BLOB字段。 · 错误:1477 SQLSTATE: HY000 (ER_CHAR_SET_IN_PART_FIELD_ERROR) 消息:如果为分区函数选择了二进制校对,才允许使用VARCHAR。 · 错误:1478 SQLSTATE: HY000 (ER_UNIQUE_KEY_NEED_ALL_FIELDS_IN_PF) 消息:在分区函数中,%s需要包含所有文件。 · 错误:1479 SQLSTATE: HY000 (ER_NO_PARTS_ERROR) 消息:%s的数目= 0不是允许的值。 · 错误:1480 SQLSTATE: HY000 (ER_PARTITION_MGMT_ON_NONPARTITIONED) 消息:无法在非分区表上进行分区管理。 · 错误:1481 SQLSTATE: HY000 (ER_DROP_PARTITION_NON_EXISTENT) 消息:分区列表中的错误出现变化。 · 错误:1482 SQLSTATE: HY000 (ER_DROP_LAST_PARTITION) 消息:不能删除所有分区,请使用DROP TABLE取而代之。 · 错误:1483 SQLSTATE: HY000 (ER_COALESCE_ONLY_ON_HASH_PARTITION) 消息:COALESCE PARTITION仅能在HASH/KEY分区上使用。 · 错误:1484 SQLSTATE: HY000 (ER_ONLY_ON_RANGE_LIST_PARTITION) 消息:%s PARTITION仅能在RANGE/LIST分区上使用。 · 错误:1485 SQLSTATE: HY000 (ER_ADD_PARTITION_SUBPART_ERROR) 消息:试图用错误的子分区数增加分区。 · 错误:1486 SQLSTATE: HY000 (ER_ADD_PARTITION_NO_NEW_PARTITION) 消息:必须至少添加1个分区。 · 错误:1487 SQLSTATE: HY000 (ER_COALESCE_PARTITION_NO_PARTITION) 消息:必须至少合并1个分区。 · 错误:1488 SQLSTATE: HY000 (ER_REORG_PARTITION_NOT_EXIST) 消息:重组的分区数超过了已有的分区数。 · 错误:1489 SQLSTATE: HY000 (ER_SAME_NAME_PARTITION) 消息:在表中,所有分区必须有唯一的名称。 · 错误:1490 SQLSTATE: HY000 (ER_CONSECUTIVE_REORG_PARTITIONS) 消息:重组分区集合时,它们必须连续。 · 错误:1491 SQLSTATE: HY000 (ER_REORG_OUTSIDE_RANGE) 消息:新分区的范围超过了已重组分区的范围。 · 错误:1492 SQLSTATE: HY000 (ER_DROP_PARTITION_FAILURE) 消息:在该版本的句柄中,不支持撤销分区。 · 错误:1493 SQLSTATE: HY000 (ER_DROP_PARTITION_WHEN_FK_DEFINED) 消息:在表上定义了外键约束时,不能舍弃分区。 · 错误:1494 SQLSTATE: HY000 (ER_PLUGIN_IS_NOT_LOADED) 消息:未加载插件'%s' 客户端错误代码和消息 客户端错误信息来自下述源文件: · 圆括号中的错误值和符号与include/errmsg.h MySQL源文件中的定义对应。 · 消息值与libmysql/errmsg.c文件中列出的错误消息对应。%d和%s分别代表数值和字符串,显示时,它 们将被消息值取代。 由于更新很频繁,这些文件中可能包含这里未列出的额外错误消息。 · 错误:2000 (CR_UNKNOWN_ERROR) 消息:未知MySQL错误。 · 错误:2001 (CR_SOCKET_CREATE_ERROR) 消息:不能创建UNIX套接字(%d) · 错误:2002 (CR_CONNECTION_ERROR) 消息:不能通过套接字'%s' (%d)连接到本地MySQL服务器。 · 错误:2003 (CR_CONN_HOST_ERROR) 消息:不能连接到'%s' (%d)上的MySQL服务器。 · 错误:2004 (CR_IPSOCK_ERROR) 消息:不能创建TCP/IP套接字(%d) · 错误:2005 (CR_UNKNOWN_HOST) 消息:未知的MySQL服务器主机'%s' (%d) · 错误:2006 (CR_SERVER_GONE_ERROR) 消息:MySQL服务器不可用。 · 错误:2007 (CR_VERSION_ERROR) 消息:协议不匹配,服务器版本= %d,客户端版本= %d · 错误:2008 (CR_OUT_OF_MEMORY) 消息:MySQL客户端内存溢出。 · 错误:2009 (CR_WRONG_HOST_INFO) 消息:错误的主机信息 · 错误:2010 (CR_LOCALHOST_CONNECTION) 消息:通过UNIX套接字连接的本地主机。 · 错误:2011 (CR_TCP_CONNECTION) 消息:%s,通过TCP/IP · 错误:2012 (CR_SERVER_HANDSHAKE_ERR) 消息:服务器握手过程中出错。 · 错误:2013 (CR_SERVER_LOST) 消息:查询过程中丢失了与MySQL服务器的连接。 · 错误:2014 (CR_COMMANDS_OUT_OF_SYNC) 消息:命令不同步,你现在不能运行该命令。 · 错误:2015 (CR_NAMEDPIPE_CONNECTION) 消息:命名管道,%s · 错误:2016 (CR_NAMEDPIPEWAIT_ERROR) 消息:无法等待命名管道,主机,%s;管道,%s (%lu) · 错误:2017 (CR_NAMEDPIPEOPEN_ERROR) 消息:无法打开命名管道,主机,%s;管道,%s (%lu) · 错误:2018 (CR_NAMEDPIPESETSTATE_ERROR) 消息:无法设置命名管道的状态,主机,%s;管道,%s (%lu) · 错误:2019 (CR_CANT_READ_CHARSET) 消息:无法初始化字符集%s (路径:%s) · 错误:2020 (CR_NET_PACKET_TOO_LARGE) 消息:获得的信息包大于'max_allowed_packet'字节。 · 错误:2021 (CR_EMBEDDED_CONNECTION) 消息:嵌入式服务器。 · 错误:2022 (CR_PROBE_SLAVE_STATUS) 消息:SHOW SLAVE STATUS出错: · 错误:2023 (CR_PROBE_SLAVE_HOSTS) 消息:SHOW SLAVE HOSTS出错: · 错误:2024 (CR_PROBE_SLAVE_CONNECT) 消息:连接到从服务器时出错: · 错误:2025 (CR_PROBE_MASTER_CONNECT) 消息:连接到主服务器时出错: · 错误:2026 (CR_SSL_CONNECTION_ERROR) 消息:SSL连接错误 · 错误:2027 (CR_MALFORMED_PACKET) 消息:残缺信息包。 · 错误:2028 (CR_WRONG_LICENSE) 消息:该客户端库仅授权给具有'%s'许可的MySQL服务器使用。 · 错误:2029 (CR_NULL_POINTER) 消息:空指针的无效使用。 · 错误:2030 (CR_NO_PREPARE_STMT) 消息:语句未准备好。 · 错误:2031 (CR_PARAMS_NOT_BOUND) 消息:没有为预处理语句中的参数提供数据。 · 错误:2032 (CR_DATA_TRUNCATED) 消息:数据截短。 · 错误:2033 (CR_NO_PARAMETERS_EXISTS) 消息:语句中不存在任何参数。 · 错误:2034 (CR_INVALID_PARAMETER_NO) 消息:无效的参数编号。 · 错误:2035 (CR_INVALID_BUFFER_USE) 消息:不能为非字符串/非二进制数据类型发送长数据(参数:%d)。 · 错误:2036 (CR_UNSUPPORTED_PARAM_TYPE) 消息:正使用不支持的缓冲区类型, %d (参数:%d) · 错误:2037 (CR_SHARED_MEMORY_CONNECTION) 消息:共享内存,%s · 错误:2038 (CR_SHARED_MEMORY_CONNECT_REQUEST_ERROR) 消息:不能打开共享内存,客户端不能创建请求事件(%lu) · 错误:2039 (CR_SHARED_MEMORY_CONNECT_ANSWER_ERROR) 消息:不能打开共享内存,未收到服务器的应答事件(%lu) · 错误:2040 (CR_SHARED_MEMORY_CONNECT_FILE_MAP_ERROR) 消息:不能打开共享内存,服务器不能分配文件映射(%lu) · 错误:2041 (CR_SHARED_MEMORY_CONNECT_MAP_ERROR) 消息:不能打开共享内存,服务器不能获得文件映射的指针(%lu) · 错误:2042 (CR_SHARED_MEMORY_FILE_MAP_ERROR) 消息:不能打开共享内存,客户端不能分配文件映射(%lu) · 错误:2043 (CR_SHARED_MEMORY_MAP_ERROR) 消息:不能打开共享内存,客户端不能获得文件映射的指针(%lu) · 错误:2044 (CR_SHARED_MEMORY_EVENT_ERROR) 消息:不能打开共享内存,客户端不能创建%s事件(%lu) · 错误:2045 (CR_SHARED_MEMORY_CONNECT_ABANDONED_ERROR) 消息:不能打开共享内存,无来自服务器的应答 (%lu) · 错误:2046 (CR_SHARED_MEMORY_CONNECT_SET_ERROR) 消息:不能打开共享内存,不能将请求事件发送到服务器(%lu) · 错误:2047 (CR_CONN_UNKNOW_PROTOCOL) 消息:错误或未知协议 · 错误:2048 (CR_INVALID_CONN_HANDLE) 消息:无效的连接句柄 · 错误:2049 (CR_SECURE_AUTH) 消息:拒绝使用旧鉴定协议(早于4.1.1)的连接(开启了客户端'secure_auth'选项)。 · 错误:2050 (CR_FETCH_CANCELED) 消息:行检索被mysql_stmt_close()调用取消。 · 错误:2051 (CR_NO_DATA) 消息:在未事先获取行的情况下试图读取列。 · 错误:2052 (CR_NO_STMT_METADATA) 消息:预处理语句不含元数据。 · 错误:2053 (CR_NO_RESULT_SET) 消息:在没有与语句相关的结果集时试图读取行。 · 错误:2054 (CR_NOT_IMPLEMENTED) 消息:该特性尚未实施 */ ?>