MySQL系统变量 sql_mode 详解

转载自:http://tech.it168.com/a2012/0822/1388/000001388401_all.shtml

MySQL数据类型:SQL_MODE设置不容忽视

        SQL_MODE多是比较容易让开发人员和DBA忽略的一个变量,默认为空。SQL_MODE的设置实际上是比较冒险的一种设置,由于在这种设置下能够容许一些非法操做,好比能够将NULL插入NOT NULL的字段中,也能够插入一些非法日期,如“2012-12-32”。所以在生产环境中强烈建议开发人员将这个值设为严格模式,这样有些问题能够在数据库的设计和开发阶段就能发现,而若是在生产环境下运行数据库后发现这类问题,那么修改的代价将变得十分巨大。此外,正确地设置SQL_MODE还能够作一些约束(Constraint)检查的工做。html

  对于SQL_MODE的设置,能够在MySQL的配置文件如my.cnf和my.ini中进行,也能够在客户端工具中进行,而且能够分别进行全局的设置或当前会话的设置。下面的命令能够用来查看当前SQL_MODE的设置状况。mysql

   mysql>  SELECT @@global.sql_mode\G  //查询全局的sql_mode变量值(全局的变量值约束的是之后全部的新的链接,直到MySQL服务器重启[重启以后就要用配置文件中的设置了],修改以后对本次链接并没有效果而只对新的链接有效)。[\G按行横向显示查询结果, \g普通显示结果,后面能够不用分号了]
  *************************** 1. row ***************************
  @@global.sql_mode:

   mysql>  SELECT @@session.sql_mode\G; //查询当前会话的sql_mode值(当前会话的变量值只约束本次链接,修改以后对本次链接当即生效)。
    或 SELECT @@sql_mode\G 【优先查询会话值,没有则查询全局值】
  *************************** 1. row ***************************
  @@session.sql_mode: NO_UNSIGNED_SUBTRACTION

  能够看到当前全局的SQL_MODE设置为空,而当前会话的设置为NO_UNSIGNED_SUBTRACTION。经过如下语句能够将当前的SQL_MODE设置为严格模式。sql

  mysql>   SET @@global.sql_mode = 'xxx'; 或者
      SET GLOBAL sql_mode='strict_trans_tables'; //设置全局的sql_mode值。
  Query OK, 0 rows affected (0.00 sec)
 
  也能够 设置当前会话的SQL_MODE的值:
  mysql>SET @@session.sql_mode = 'xxxx'; 或者
      SET SESSION SQL_MODE='PAD_CHAR_TO_FULL_LENGTH'; 或者
  mysql>SET SQL_MODE='PAD_CHAR_TO_FULL_LENGTH';【这一句是优先设置会话值,若是会话中没有此变量则设置全局值】
 

  严格模式是指将SQL_MODE变量设置为STRICT_TRANS_TABLES或STRICT_ALL_TABLES中的至少一种。如今来看一下SQL_MODE能够设置的选项。数据库

  •   STRICT_TRANS_TABLES:在该模式下,若是一个值不能插入到一个事务表(例如表的存储引擎为InnoDB)中,则中断当前的操做。不影响非事务表(例如表的存储引擎为MyISAM)。
  •   ALLOW_INVALID_DATES:该选项并不彻底对日期的合法性进行检查,只检查月份是否在1~12之间,日期是否在1~31之间。该模式仅对DATE和DATETIME类型有效,而对TIMESTAMP无效,由于TIMESTAMP老是要求一个合法的输入。
  •   ANSI_QUOTES:默认状况下,MySQL的关键词以及表名等均可以有反引号``引发来。启用ANSI_QUOTES后,即可以用双引号代替反引号。固然原来的反引号依然可用。可是引起的一个反作用是:不能用双引号来引用字符串,由于它将被  解释为识别符。解决方法就是使用单引号来表示字符串。示例以下:

    mysql> CREATE TABLE z ( a VARCHAR(10))ENGINE=INNODB;服务器

    mysql>INSERT INTO z SELECT "aaa";session

    mysql> SET sql_mode='ANSI_QUOTES';函数

    mysql> INSERT INTO z SELECT "aaa"; // 'aaa'是能够的工具

    ERROR 1054 (42S22): Unknown column 'aaa' in 'field list'测试

  •   ERROR_FOR_DIVISION_BY_ZERO:在INSERT或UPDATE过程当中,若是数据被零除(或MOD(X,0)),则产生错误(不然为警告)。若是未给出该模式,那么数据被零除时MySQL返回NULL。若是用到INSERT IGNORE或UPDATE IGNORE    中,MySQL生成被零除警告,但操做结果为NULL。
  •   HIGH_NOT_PRECEDENCE NOT:操做符的优先顺序是表达式。例如,NOT a BETWEEN b AND c被解释为NOT(a BETWEEN b AND c),在一些旧版本MySQL中, 前面的表达式被解释为(NOT a)BETWEEN b AND c。启用    HIGH_NOT_PRECEDENCE SQL模式,能够得到之前旧版本的更高优先级的结果。下面看一个例子:

     mysql> SELECT 0 BETWEEN -1 AND 1\G;spa

  *************************** 1. row ***************************
  0 BETWEEN -1 AND 1: 1

  0在-1到1之间,因此返回1,若是加上NOT,则返回0,过程以下:

  
  mysql> SELECT @@sql_mode\G;
  *************************** 1. row ***************************
  @@sql_mode:
  
  mysql> SELECT not 0 BETWEEN -1 AND 1\G;
  *************************** 1. row ***************************
  NOT 0 BETWEEN -1 AND 1: 0

  可是若是启用HIGH_NOT_PRECEDENCE模式,则SELECT NOT 0 BETWEEN -1 AND 1被解释为SELECT(NOT 0)BETWEEN -1 AND 1,结果就彻底相反,以下所示:

  
  mysql> SELECT NOT 0 BETWEEN -1 AND 1\G;
  *************************** 1. row ***************************
  NOT 0 BETWEEN -1 AND 1: 1  

  从上述例子中还能看出,在MySQL数据库中BETWEEN a AND b被解释为[a,b]。下面作两个简单的测试。

  mysql> SELECT 1 BETWEEN -1 AND 1\G;
  *************************** 1. row ***************************
  1 BETWEEN -1 AND 1: 1
  mysql> SELECT -1 BETWEEN -1 AND 1\G;
  *************************** 1. row ***************************
  -1 BETWEEN -1 AND 1: 1
  •   IGNORE_SPACE:  参考 http://www.javashuo.com/article/p-uekivkak-y.html
  •   NO_AUTO_CREATE_USER:禁止GRANT建立密码为空的用户。
  •   NO_AUTO_VALUE_ON_ZERO:该选项影响列为自增加的插入。在默认设置下,若是自增加字段插入了0或NULL,其实是生成了下一个自增加值。启用此选项后,只有NULL能够自动生成下一个序列号。此时自增加字段插入0是能够正常插入的。
  •   NO_BACKSLASH_ESCAPES:反斜杠“\”做为普通字符而非转义符,示例以下:
  mysql> SET sql_mode='';
  mysql> SELECT '\\'\G;
  *************************** 1. row ***************************
  \: \  [一个键一个值]
  mysql> SET sql_mode='NO_BACKSLASH_ESCAPES';
  mysql> SET '\\'\G;
  *************************** 1. row ***************************
  \\: \\

  •   NO_DIR_IN_CREATE:在建立表时忽视全部INDEX DIRECTORY和DATA DIRECTORY的选项。
  •   NO_ENGINE_SUBSTITUTION:若是须要的存储引擎被禁用或未编译,那么抛出错误。默认用默认的存储引擎替代,并抛出一个异常。
  •   NO_UNSIGNED_SUBTRACTION:以前已经介绍过,启用这个选项后两个UNSIGNED类型相减返回SIGNED类型。
  •   NO_ZERO_DATE:在非严格模式下,能够插入形如“0000-00-00 00:00:00”的非法日期,MySQL数据库仅抛出一个警告。而启用该选项后,MySQL数据库不容许插入零日期,插入零日期会抛出错误而非警告。
  •   NO_ZERO_IN_DATE:在严格模式下,不容许日期和月份为零。如“2011-00-01”和“2011-01-00”这样的格式是不容许的。采用日期或月份为零的格式时MySQL都会直接抛出错误而非警告,示例以下:
  mysql> SET sql_mode='NO_ZERO_IN_DATE';
  mysql> CREATE TABLE a ( a DATETIME );
  mysql> INSERT INTO a SELECT '2011-01-00';
  ERROR 1292 (22007): Incorrect datetime value: '2011-01-00' for column 'a' at row 1

  ONLY_FULL_GROUP_BY:对于GROUP BY聚合操做,若是在SELECT中的列,没有在GROUP BY中出现,那么这个SQL是不合法的,由于列不在GROUP BY从句中,因此对于设置了这个mode的数据库,在使用group by 的时候,就要用MAX(),SUM(),ANT_VALUE()这种聚合函数,才能完成GROUP BY 的聚合操做。。示例以下:

  mysql> SET sql_mode='ONLY_FULL_GROUP_BY';
  mysql> SELECT a,SUM(b) FROM t GROUP BY b;
  ERROR 1055 (42000): 'test.t.a' isn't in GROUP BY
  •   PAD_CHAR_TO_FULL_LENGTH:对于CHAR类型字段,不要截断空洞数据。空洞数据就是自动填充值为0x20的数据。先来看MySQL数据库在默认状况下的表现。
  mysql> CREATE TABLE t ( a CHAR(10) );
  mysql> INSERT INTO t SELECT 'a';
  mysql>SELECT a,CHAR_LENGTH(a),HEX(a) FROM t\G;
  *************************** 1. row ***************************
  a: a
  CHAR_LENGTH(a): 1
  HEX(a): 61
  1 row in set (0.04 sec)

  能够看到,在默认状况下,虽然a列是CHAR类型,可是返回的长度是1,这是由于MySQL数据库已经对后面的空洞数据进行了截断。若启用PAD_CHAR_TO_FULL_LENGTH选项,则反映的是实际存储的内容,例如:

  mysql> SELECT a,CHAR_LENGTH(a),HEX(a) FROM t\G;
  *************************** 1. row ***************************
  a: a
  CHAR_LENGTH(a): 10
  HEX(a): 61202020202020202020
  1 row in set (0.00 sec)

  能够看到在CHAR列a中实际存储的值为0x61202020202020202020。

  •   PIPES_AS_CONCAT:将“||”视为字符串的链接操做符而非或运算符,这和Oracle数据库是同样的,也和字符串的拼接函数Concat相相似,例如:
  mysql> SET sql_mode='pipes_as_concat';
  mysql> SELECT 'a'||'b'||'c'\G;
  *************************** 1. row ***************************
  'a'||'b'||'c': abc
  1 row in set (0.00 sec)
  •   REAL_AS_FLOAT:将REAL视为FLOAT的同义词,而不是DOUBLE的同义词。
  •   STRICT_ALL_TABLES:对全部引擎的表都启用严格模式。(STRICT_TRANS_TABLES只对支持事务的表启用严格模式)。

  在严格模式下,一旦任何操做的数据产生问题,都会终止当前的操做。对于启用STRICT_ALL_TABLES选项的非事务引擎来讲,这时数据可能停留在一个未知的状态。这可能不是全部非事务引擎愿意看到的一种状况,所以须要很是当心这个选项可能带来的潜在影响。

  下面的几种SQL_MODE设置是以前讨论的几种选项的组合。

  •   ANSI:等同于REAL_AS_FLOAT、PIPES_AS_CONCAT和ANSI_QUOTES、IGNORE_SPACE的组合。
  •   ORACLE:等同于PIPES_AS_CONCAT、 ANSI_QUOTES、IGNORE_SPACE、 NO_KEY_OPTIONS、 NO_TABLE_OPTIONS、 NO_FIELD_OPTIONS和NO_AUTO_CREATE_USER的组合。
  •   TRADITIONAL:等同于STRICT_TRANS_TABLES、 STRICT_ALL_TABLES、NO_ZERO_IN_DATE、NO_ZERO_DATE、 ERROR_FOR_DIVISION_BY_ZERO、NO_AUTO_CREATE_USER和 NO_ENGINE_SUBSTITUTION的组合。
  •   MSSQL:等同于PIPES_AS_CONCAT、 ANSI_QUOTES、 IGNORE_SPACE、NO_KEY_OPTIONS、NO_TABLE_OPTIONS和 NO_FIELD_OPTIONS的组合。
  •   DB2:等同于PIPES_AS_CONCAT、ANSI_QUOTES、 IGNORE_SPACE、NO_KEY_OPTIONS、 NO_TABLE_OPTIONS和NO_FIELD_OPTIONS的组合。
  •   MYSQL323:等同于NO_FIELD_OPTIONS和HIGH_NOT_PRECEDENCE的组合。
  •   MYSQL40:等同于NO_FIELD_OPTIONS和HIGH_NOT_PRECEDENCE的组合。
  •   MAXDB:等同于PIPES_AS_CONCAT、ANSI_QUOTES、IGNORE_SPACE、NO_KEY_OPTIONS、 NO_TABLE_OPTIONS、 NO_FIELD_OPTIONS和 NO_AUTO_CREATE_USER的组合。
相关文章
相关标签/搜索