服务器 频道

MySQL常见错误问答

  18.13 时区问题

  如果你有一个问题,SELECT NOW()以GMT时间返回值而不是你的本地时间,你必须设定TZ环境变量为你的当前时区。这应该在服务器运行的环境进行,例如在safe_mysqld或mysql.server中。

  18.14 在搜索中的大小写敏感性

  缺省地,MySQL搜索是大小写不敏感的(尽管有一些字符集从来不是忽略大小写的,例如捷克语)。这意味着,如果你用col_name LIKE ’a%’搜寻,你将得到所有以A或a开始的列值。如果你想要使这个搜索大小写敏感,使用象INDEX(col_name, "A")=0检查一个前缀。或如果列值必须确切是"A",使用STRCMP(col_name, "A") = 0。

  简单的比较操作(>=、>、= 、< 、<=、排序和聚合)是基于每个字符的“排序值”。有同样排序值的字符(象E,e和’e)被视为相同的字符!

  LIKE比较在每个字符的大写值上进行(E==e 但是E<>’e)。

  如果你想要一个列总是被当作大小写敏感的方式,声明它为BINARY。见7.7 CREATE TABLE句法。

  如果你使用以所谓的big5编码的中文数据,你要使所有的字符列是BINARY,它可行,是因为big5编码字符的排序顺序基于 ASCII代码的顺序。

  18.15 NULL值问题

  NULL值的概念是造成SQL的新手的混淆的普遍原因,他们经常认为NULL是和一个空字符串’’的一样的东西。不是这样的!例如,下列语句是完全不同的:

  mysql> INSERT INTO my_table (phone) VALUES (NULL);

  mysql> INSERT INTO my_table (phone) VALUES ("");

  两个语句把值插入到phone列,但是第一个插入一个NULL值而第二个插入一个空字符串。第一个的含义可以认为是“电话号码不知道”,而第二个则可意味着“她没有电话”。 

  在SQL中,NULL值在于任何其他值甚至NULL值比较时总是假的(FALSE)。包含NULL的一个表达式总是产生一个NULL值,除非在包含在表达式中的运算符和函数的文档中指出。在下列例子,所有的列返回NULL:

  mysql> SELECT NULL,1+NULL,CONCAT(’Invisible’,NULL);

  如果你想要寻找值是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; 

  WHERE语句被执行以确定哪些行应该包括GROUP BY部分中,而HAVING用来决定应该只用结果集合中的哪些行。  

  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。 

0
相关文章