18.2.1 MySQL server has gone away错误 本小节也涉及有关Lost connection to server during query的错误。 对MySQL server has gone away错误最常见的原因是服务器超时了并且关闭了连接。缺省地,如果没有事情发生,服务器在 8个小时后关闭连接。你可在启动mysqld时通过设置wait_timeout变量改变时间限制。 你可以通过执行mysqladmin version并且检验正常运行的时间来检查MySQL还没死掉。 如果你有一个脚本,你只须再发出查询让客护进行一次自动的重新连接。 在这种请下,你通常能获得下列错误代码(你得到的是OS相关的): CR_SERVER_GONE_ERROR 客户不能发送一个问题给服务器。 CR_SERVER_LOST 当写服务器时,客户没有出错,但是它没有得到对问题的一个完整的答案(或任何答案)。
18.2.2 Can’t connect to [local] MySQL server错误 一个MySQL客户可以两种不同的方式连接mysqld服务器:Unix套接字,它通过在文件系统中的一个文件(缺省“/tmp/mysqld.sock”)进行连接;或TCP/IP,它通过一个端口号连接。Unix套接字比TCP/IP更快,但是只有用在连接同一台计算机上的服务器。如果你不指定主机名或如果你指定特殊的主机名localhost,使用Unix套接字。 错误(2002)Can’t connect to ...通常意味着没有一个MySQL服务器运行在系统上或当试图连接mysqld服务器时,你正在使用一个错误的套接字文件或TCP/IP端口。 由检查(使用ps)在你的服务器上有一个名为mysqld的进程启动!如果没有任何mysqld过程,你应该启动一个。见4.15.2 启动MySQL服务器的问题。 如果一个mysqld过程正在运行,你可以通过尝试这些不同的连接来检查服务器(当然,端口号和套接字路径名可能在你的安装中是不同的): shell> mysqladmin version shell> mysqladmin variables shell> mysqladmin -h `hostname` version variables shell> mysqladmin -h `hostname` --port=3306 version shell> mysqladmin -h ’ip for your host’ version shell> mysqladmin --socket=/tmp/mysql.sock version
注意hostname命令使用反引号“`”而非正引号“’”;这些导致hostname输出(即,当前主机名)被代替进mysqladmin命令中。 这是可能造成Can’t connect to local MySQL server错误的一些原因: mysqld不在运行。 你正在使用MIT-pthreads的一个系统上运行。如果正在运行在一个没有原生线程的系统上,mysqld使用 MIT-pthreads 软件包。见4.2 由MySQL支持的操作系统。然而,MIT-pthreads不支持Unix套接字,因此当与服务器连接时,在这样一个系统上,你总是必须明确地指定主机名。试试使用这个命令检查到服务器的连接: shell> mysqladmin -h `hostname` version
你也可以使用--socket=/path/to/socket选项启动safe_mysqld和在启动你的MySQL客户前设置环境变量MYSQL_UNIX_PORT为套接字路径名。你可用--socket=/path/to/socket选项启动mysqld服务器。如果你改变了服务器的套接字路径名,你也必须通知MySQL客户关于新路径的情况。你可以通过设置环境变量MYSQL_UNIX_PORT为套接字路径名或由提供套接字路径名作为客户的参数做到。你可用这个命令测试套接字: shell> mysqladmin --socket=/path/to/socket version
你正在使用 Linux和线程已经死了(核心倾倒了)。在这种情况中,你必须杀死其它mysqld线程(例如在启动一个新的MySQL服务器之前,可以用mysql_zap脚本)。见18.1 如果MySQL总是崩溃怎么办。 如果你得到错误Can’t connect to MySQL server on some_hostname,你可以尝试下列步骤找出问题是什么: 通过执行telnet your-host-name tcp-ip-port-number并且按几次回车来检查服务器是否正常运行。如果有一个MySQL运行在这个端口上,你应该得到一个包含正在运行的MySQL服务器的版本号的应答。如果你得到类似于telnet: Unable to connect to remote host: Connection refused的一个错误,那么没有服务器在使用的端口上运行。 尝试连接本地机器上的mysqld守护进程,并用mysqladmin variables检查mysqld被配置使用的TCP/IP端口(变量port)。 检查你的mysqld服务器没有用--skip-networking选项启动。
18.2.3 Host ’...’ is blocked错误 如果你得到象这样的一个错误: Host ’hostname’ is blocked because of many connection errors. Unblock with ’mysqladmin flush-hosts’
18.2.4 Too many connections错误 如果在你试土连接MySQL时,你得到错误Too many connections,这意味着已经有max_connections个客户连接了mysqld服务器。 如果你需要比缺省(100)更多的连接,那么你应该重启mysqld,用更大的 max_connections 变量值。 注意,mysqld实际上允许(max_connections+1)个客户连接。最后一个连接是为一个用Process权限的用户保留的。通过不把这个权限给一般用户(他们不应该需要它),有这个权限一个管理员可以登录并且使用SHOW PROCESSLIST找出什么可能出错。见7.21 SHOW句法(得到表,列的信息)。 18.2.5 Out of memory错误 如果你发出查询并且得到类似于下面的错误: mysql: Out of memory at line 42, ’malloc.c’ mysql: needed 8136 byte (8k), memory in use: 12481367 bytes (12189k) ERROR 2008: MySQL client ran out of memory
18.2.6 Packet too large错误 当一个MySQL客户或mysqld服务器得到一个比max_allowed_packet个字节长的包,它发出一个Packet too large错误并终止连接。 如果你正在使用mysql客户,你可以通过用mysql --set-variable=max_allowed_packet=8M指定一个更大的缓冲区来启动客户程序。 如果你正在使用不允许你指定最大包大小的其他客户(例如 DBI),你需要在你启动服务器时设置包大小。你可以使用mysqld的命令行选项设置max_allowed_packet为一个更大的尺寸。例如,如果你正期望将一个全长的BLOB存入一张表中,你将需要用--set-variable=max_allowed_packet=24M选项来启动服务器。
18.2.7 The table is full错误 这个错误发生在内存临时表变得比tmp_table_size字节大时。为了避免这个问题,你可以使用mysqld的-O tmp_table_size=#选项来增加临时表的大小,或在你发出有疑问的查询之前使用SQL选项SQL_BIG_TABLES。见7.25 SET OPTION句法。 你也可以使用--big-tables选项启动mysqld。这与为所有查询使用SQL_BIG_TABLES完全相同。 18.2.8 Commands out of sync in client错误 如果你在你的客户代码中得到Commands out of sync; You can’t run this command now,你正在以错误的次序调用客户函数! 这可能发生,例如,如果你正在使用mysql_use_result()并且在你已经调用了mysql_free_result()之前试图执行新查询。如果你在mysql_use_result()或mysql_store_result()之间试图执行返回数据的2个查询,它也可能发生。
18.2.9 Ignoring user错误 如果你得到下列错误: Found wrong password for user: ’some_user@some_host’; Ignoring user 这意味着在mysqld启动时或在它再次装载权限表时,它在user表中找到了一个有一个无效口令的条目。结果,条目简单地被权限系统忽略。 可能导致这个问题的原因和修正: 你可能正在运行一个有一个老的user表的新版本mysqld。你可以通过执行mysqlshow mysql user看看口令字段是否少于 16个字符来检查它。如果是这样,你可以通过运行scripts/add_long_password脚本改正这种情况。 用户有一个老式的口令(8个字符长)并且你没使用--old-protocol选项启动mysqld。用一个新口令更新在user表中的用户或用--old-protocol重启mysqld。 你没有使用PASSWORD()函数在在user表中指定了一个口令。使用mysql以一个新口令更新在user表中的用户。确保使用PASSWORD()函数: mysql> update user set password=PASSWORD(’your password’) where user=’XXX’;
18.11 文件没找到 如果你从MySQL得到ERROR ’...’ not found (errno: 23), Can’t open file: ... (errno: 24)或任何其他有errno 23或errno 24的错误,它意味着,你没有为MySQL分配足够的文件描述符。你能使用perror实用程序得到错误号含义是什么的描述: shell> perror 23 File table overflow shell> perror 24 Too many open files
18.12 使用DATE列的问题 一个DATE值的格式是’YYYY-MM-DD’。根据ANSI SQL,不允许其他格式。你应该在UPDATE表达式和SELECT语句的WHERE子句中使用这个格式。例如: mysql> SELECT * FROM tbl_name WHERE date >= ’1997-05-05’;
为了方便,如果日期用在数字上下文,MySQL自动变换一个日期到一个数字(并且反过来也如此)。当更新时和将一个日期与TIMESTAMP、DATE或DATETIME列比较的一个WHERE子句中,也是足够灵活以允许一种“宽松”的字符串格式。(宽松格式意味着任何标点字符用作在部件之间的分割符。例如,’1998-08-15’和’1998#08#15’是等价的。)MySQL也能变换不包含分割符的一个字符串(例如 ’19980815’),如果它作为一个日期说得通。特殊日期’0000-00-00’可以作为’0000-00-00’被存储和检索。当通过MyODBC使用一个’0000-00-00’日期时,在MyODBC 2.50.12和以上版本,它将自动被转换为NULL,因为ODBC不能处理这种日期。 因为MySQL实行了上述的变换,下列语句可以工作: mysql> INSERT INTO tbl_name (idate) VALUES (19970505); mysql> INSERT INTO tbl_name (idate) VALUES (’19970505’); mysql> INSERT INTO tbl_name (idate) VALUES (’97-05-05’); mysql> INSERT INTO tbl_name (idate) VALUES (’1997.05.05’); mysql> INSERT INTO tbl_name (idate) VALUES (’1997 05 05’); mysql> INSERT INTO tbl_name (idate) VALUES (’0000-00-00’);
mysql> SELECT idate FROM tbl_name WHERE idate >= ’1997-05-05’; mysql> SELECT idate FROM tbl_name WHERE idate >= 19970505; mysql> SELECT mod(idate,100) FROM tbl_name WHERE idate >= 19970505; mysql> SELECT idate FROM tbl_name WHERE idate >= ’19970505’;
然而,下列将不工作: mysql> SELECT idate FROM tbl_name WHERE STRCMP(idate,’19970505’)=0;
如果你想要寻找值是NULL的列,你不能使用=NULL测试。下列语句不返回任何行,因为对任何表达式,expr = NULL是假的: mysql> SELECT * FROM my_table WHERE phone = NULL;
要想寻找NULL值,你必须使用IS NULL测试。下例显示如何找出NULL电话号码和空的电话号码: mysql> SELECT * FROM my_table WHERE phone IS NULL; mysql> SELECT * FROM my_table WHERE phone = "";
在MySQL中,就像很多其他的SQL服务器一样,你不能索引可以有NULL值的列。你必须声明这样的列为NOT NULL,而且,你不能插入NULL到索引的列中。当用LOAD DATA INFILE读取数据时,空列用’’更新。如果你想要在一个列中有NULL值,你应该在文本文件中使用\N。字面词’NULL’也可以在某些情形下使用。见7.16 LOAD DATA INFILE句法。当使用ORDER BY时,首先呈现NULL值。如果你用DESC以降序排序,NULL值最后显示。当使用GROUP BY时,所有的NULL值被认为是相等的。为了有助于NULL的处理,你能使用IS NULL和IS NOT NULL运算符和IFNULL()函数。 对某些列类型,NULL值被特殊地处理。如果你将NULL插入表的第一个TIMESTAMP列,则插入当前的日期和时间。如果你将NULL插入一个AUTO_INCREMENT列,则插入顺序中的下一个数字。 18.16 alias问题 你可以在GROUP BY、ORDER BY或在HAVING部分中使用别名引用列。别名也可以用来为列取一个更好点的名字: SELECT SQRT(a*b) as rt FROM table_name GROUP BY rt HAVING rt > 0; SELECT id,COUNT(*) AS cnt FROM table_name GROUP BY id HAVING cnt > 0; SELECT id AS "Customer identity" FROM table_name;
注意,你的 ANSI SQL 不允许你在一个WHERE子句中引用一个别名。这是因为在WHERE代码被执行时,列值还可能没有终结。例如下列查询是不合法:SELECT id,COUNT(*) AS cnt FROM table_name WHERE cnt > 0 GROUP BY id;
18.17 从关联的表中删除行 因为MySQL不支持子选择或在DELETE语句中使用多个表,你应该使用下列方法从2个关联的表中删除行: 在主表中基于某个WHERE条件SELECT行。 在主表中基于相同的条件DELETE行。 DELETE FROM related_table WHERE related_column IN (selected_rows) 如果在related_column查询中的字符的全部数量超过1,048,576(缺省值max_allowed_packet),你应该分成更小的部分并且执行多个DELETE语句。如果related_column是一个索引,你每次只删除100-1000个related_column id将可能使得DELETE最快。如果related_column不是一个索引,速度与IN子句中参数的数量无关。
18.18 解决没有匹配行的问题 如果你有一个复杂的查询,涉及多个表,但没有返回任何行,你应该使用下列过程查找你的询问有什么不对: EXPLAIN测试查询并且检查你是否能找出显然是错误的一些东西。见7.22 EXPLAIN句法(得到关于一个SELECT的信息)。 仅选择那些在WHERE子句中使用的字段。 一次从查询中删除一个表,直到它返回一些行。如果表很大,对查询使用LIMIT 10是一个好主意。 对应该已经匹配一行的列做一个SELECT,针对从询问中做后被删除的表。 如果你将FLOAT或DOUBLE列与有小数的数字进行比较,你不能使用=!。这个问题在大多数计算机语言是常见的,因为浮点值不是准确的值。 mysql> SELECT * FROM table_name WHERE float_column=3.5; -> mysql> SELECT * FROM table_name WHERE float_column between 3.45 and 3.55;
在大多数情况下,将FLOAT改成一个DOUBLE将修正它! 如果你仍然不能发现错误是什么,创建一个最小的可运行mysql test < query.sql的测试来显示你的问题。你可以用mysqldump --quick database tables > query.sql创建一个测试文件,在一个编辑器编辑文件,删除一些插入行(如果有太多这些语句)并且在文件末尾加入你的选择语句。测试你仍然有问题,可以这样做: shell> mysqladmin create test2 shell> mysql test2 < query.sql
使用mysqlbug的邮寄测试文件到mysql@lists.mysql.com。
18.19 与ALTER TABLE有关的问题 如果ALTER TABLE死于这样一个错误: Error on rename of ’./database/name.frm’ to ’./database/B-a.frm’ (Errcode: 17)
问题可能是MySQL在前一个ALTER TABLE中已经崩溃并且留下了一个名为“A-xxx”或“B-xxx”的老的数据库表。在这种情况下,到MySQL数据目录中并删除所有名字以A-或B-开始的文件。(你可以把他们移到别的地方而不是删除他们)。 ALTER TABLE工作方式是: 以要求的改变创建一个名为“A-xxx”的新表。 从老表把所有行拷贝到“A-xxx”。 老表被改名为“B-xxx”。 “A-xxx”被改名为你的老表的名字。 “B-xxx”被删除。 如果某些改名操作出错,MySQL试图还原改变。如果出错严重(当然,这不应该发生。),MySQL可能留下了老表为“B-xxx”但是一个简单改名就应该恢复你的数据。
18.20 怎样改变一张表中列的顺序 SQL的要点是中抽象应用程序以避免数据存储格式。你应该总是以你想要检索数据的意愿指定顺序。例如: SELECT col_name1, col_name2, col_name3 FROM tbl_name;
将以col_name1、col_name2、col_name3的顺序返回列,而: SELECT col_name1, col_name3, col_name2 FROM tbl_name;
将以col_name1、col_name3、col_name2的顺序返回列。 在一个应用程序中,你应该决不基于他们的位置使用SELECT * 检索列,因为被返回的列的顺序永远不能保证;对你的数据库的一个简单改变可能导致你的应用程序相当有戏剧性地失败。 不管怎样,如果你想要改变列的顺序,你可以这样做: 以正确的列顺序创建一张新表。 执行INSERT INTO new_table SELECT fields-in-new_table-order FROM old_table. 删除或改名old_table。 ALTER TABLE new_table RENAME old_table。