不用不知道,用了没用?

昨天在线上创建了一个表,其中有两个列是timestamp类型的,创建语句假设是这样的:

create table timetest(id int, createtime timestamp,updatetime timestamp);

但是在创建完成之后,显示一下它的创建语句show create table timetest;

CREATE TABLE `timetest` (
`id` int(11) DEFAULT NULL,
`createtime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`updatetime` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4

从上面的结果可以看到,createtime,updatetime多了默认值,第一个默认值为CURRENT_TIMESTAMP,就是说只要修改当前这条记录,那么这个列的值就会被改为now,而第二个列的默认值为'0000-00-00 00:00:00',嗯?怎么回事,完全没有道理!
后面查看了有关文档,发现修改为CURRENT_TIMESTAMP是有原因的,因为很多情况下,一个表中会存在一个修改时间的列,只要这个对象被更新了,那么这个列相应自动修改为当前时间,这是一个比较人性化的针对懒人设置的(源码中可以看到),这个倒是可以理解。
本人之前用数据库比较少,这个特性不太清楚,所以刚好在建表的时候将创建时间放在第一个,将更新时间放在第二个,而据我了解到,自动更新为当前时间的是表中第一个timestamp列,所以刚好将createtime列的设置值设置为CURRENT_TIMESTAMP,并且更新的时候也会更新为CURRENT_TIMESTAMP。
从名字可以看到,这个列是createtime,显然这个值是不会变的,而真正需要这个特性的是updatetime列。

那这个表已经在线上创建了,可能已经在用了,怎么办?总不能删除再创建吧?

然后打算修改一下这个列的default值,语句如下:

alter table timetest alter createtime set default '0000-00-00 00:00:00';

执行后结果会很让人出忽意料,此时再做show create table timetest;
CREATE TABLE `timetest` (
`id` int(11) DEFAULT NULL,
`createtime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`updatetime` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4

嗯?怎么还是一样的?为什么?

看来只能从源码入手了,因为没有任何报错信息,显示的是成功了。

在mysql_alter_table函数中有下面一段代码:
if (!thd->variables.explicit_defaults_for_timestamp)
  promote_first_timestamp_column(&alter_info->create_list);

从名字就可以看到,它是将第一个timestamp列的default值提升为CURRENT_TIMESTAMP,函数实现如下:

void promote_first_timestamp_column(List<Create_field> *column_definitions)
{
  List_iterator<Create_field> it(*column_definitions);
  Create_field *column_definition;

  while ((column_definition= it++) != NULL)
  {
    if (column_definition->sql_type == MYSQL_TYPE_TIMESTAMP || // TIMESTAMP
      column_definition->sql_type == MYSQL_TYPE_TIMESTAMP2 || // ms TIMESTAMP
      column_definition->unireg_check == Field::TIMESTAMP_OLD_FIELD) // Legacy
      {
        if ((column_definition->flags & NOT_NULL_FLAG) != 0 && // NOT NULL,
          column_definition->def == NULL && // no constant default,
          column_definition->unireg_check == Field::NONE) // no function default
        {
          DBUG_PRINT("info", ("First TIMESTAMP column '%s' was promoted to "
          "DEFAULT CURRENT_TIMESTAMP ON UPDATE "
          "CURRENT_TIMESTAMP",
          column_definition->field_name));
          column_definition->unireg_check= Field::TIMESTAMP_DNUN_FIELD;
        }
        return;
      }
   }
}

可以看出,如果有一个列为timestamp,且没有指定default值(column_definition->def == NULL),且column_definition->unireg_check == Field::NONE时,就会将column_definition->unireg_check值修改为TIMESTAMP_DNUN_FIELD,说明如果某一个列有这个值,则它表示的就是DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,但是通过跟踪代码发现,这个函数进来之后,根本没有执行这个赋值语句,因为column_definition->unireg_check的值已经是Field::TIMESTAMP_DNUN_FIELD了,为什么已经是这个值呢?因为在修改这个列的时候,新列的unireg_check值是复制的原来列的值,而新列的column_definition->def值是不为空的,它是'0000-00-00 00:00:00',那么最终的结果就是,这个列被修改为unireg_check为TIMESTAMP_DNUN_FIELD,同时def为'0000-00-00 00:00:00'值的列。

那么现在看看更新操作是如何做的
在mysql_update中有下面的2行代码:
if (update.add_function_default_columns(table, table->write_set))
  DBUG_RETURN(1);

这里面所做的工作就是将所有的属性为Field::TIMESTAMP_DNUN_FIELD的timestamp类型的列自动加入更新为CURRENT_TIMESTAMP的操作,那么从这里可以看出,只要是有Field::TIMESTAMP_DNUN_FIELD属性的列,那么它这个列就具有了DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP属性。

还可以从show create table timetest;中查看它是如何判断是不是要输出DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP:

在函数print_default_clause中,有如下代码段:

static bool print_default_clause(THD *thd, Field *field, String *def_value,
bool quoted)
{
  enum enum_field_types field_type= field->type();

  const bool has_now_default= field->has_insert_default_function();
  if (has_default)
  {
    if (has_now_default)
    {
      def_value->append(STRING_WITH_LEN("CURRENT_TIMESTAMP"));
      ...
    }
    ...
  }
  ...
}

而函数has_insert_default_function的实现:
bool has_insert_default_function() const
{
return unireg_check == TIMESTAMP_DN_FIELD ||
unireg_check == TIMESTAMP_DNUN_FIELD;
}

这里更加可以坚定的证实它完全是通过TIMESTAMP_DNUN_FIELD来判断的。

那么现在回到上面修改失败的问题,其实还是promote_first_timestamp_column函数的问题,因为里面它只考虑了unireg_check值,而没有考虑是不是这个列的默认值column_definition->def为空,在def及unireg_check之间,应该是def优先级更高的,所以这里应该再做一个降级的,只要def不为空,就需要将unireg_check丢弃,而这里没有做任何处理。

也许这里正是这个问题的结症所在。

那这个问题如果不修改的话有解决办法么?经过应钢同学的指点,它给我如下语句:
语法:MODIFY [COLUMN] col_name column_definition
语句:alter table timetest modify createtime timestamp NOT NULL DEFAULT '0000-00-00 00:00:00';
执行完成之后,再做show create table timetest;
CREATE TABLE `timetest` (
`id` int(11) DEFAULT NULL,
`createtime` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`updatetime` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4

哇,修改过来了,但是这个修改方式是将所有的这个列的定义都改了,所以不会继承任何东西。

另一个解决方式:
也许有人已经发现了,上面的代码中有这样的行:
if (!thd->variables.explicit_defaults_for_timestamp)
promote_first_timestamp_column(&alter_info->create_list);
这里有一个条件,就是判断会话参数explicit_defaults_for_timestamp,如果设置为1则不转换,就不会出现第一个timestamp类型的列被自动设置为DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP了,而下面看看这个参数的信息:

/**
This variable is read only to users. It can be enabled or disabled
only at mysqld startup. This variable is used by User thread and
as well as by replication slave applier thread to apply relay_log.
Slave applier thread enables/disables this option based on
relay_log's from replication master versions. There is possibility of
slave applier thread and User thread to have different setting for
explicit_defaults_for_timestamp, hence this options is defined as
SESSION_VAR rather than GLOBAL_VAR.
*/
static Sys_var_mybool Sys_explicit_defaults_for_timestamp(
"explicit_defaults_for_timestamp",
"This option causes CREATE TABLE to create all TIMESTAMP columns "
"as NULL with DEFAULT NULL attribute, Without this option, "
"TIMESTAMP columns are NOT NULL and have implicit DEFAULT clauses. "
"The old behavior is deprecated.",
READ_ONLY SESSION_VAR(explicit_defaults_for_timestamp),
CMD_LINE(OPT_ARG), DEFAULT(FALSE));

默认值为FALSE,并且是一个只读的会话变量,只能通过系统启动时设置这个值。
那么如果设置了explicit_defaults_for_timestamp=1,则不会自动转换。

结论:
alter table timetest alter createtime set default '0000-00-00 00:00:00';
这个方式不起作用,所谓不用不知道,用了没用。
alter table timetest modify createtime timestamp NOT NULL DEFAULT '0000-00-00 00:00:00';
这个方式修改所有的列类型
explicit_defaults_for_timestamp=1
设置了这个完全关闭这个功能。

突然用mysql多了还真现不少问题,继续发现,继续解决。

mysql Alter table设置default的问题,是bug么?的更多相关文章

  1. MySQL ALTER TABLE: ALTER vs CHANGE vs MODIFY COLUMN

    ALTER COLUMN 语法: ALTER [COLUMN] col_name {SET DEFAULT literal | DROP DEFAULT} 作用: 设置或删除列的默认值.该操作会直接修 ...

  2. mysql ALTER TABLE语句 语法

    mysql ALTER TABLE语句 语法 作用:用于在已有的表中添加.修改或删除列.无铁芯直线电机 语法:添加列:ALTER TABLE table_name ADD column_name da ...

  3. 【待整理】MySQL alter table modify vs alter table add产生state不一样

    MySQL:5.6.35 OS:redhat5.8 今天更新数据库某些表字段,有如下两SQL: ①alter table xx modify xxxx;(表大概是77w) ②alter table s ...

  4. MySQL ALTER TABLE语法

    先看一下定义(密密麻麻) ALTER TABLE tbl_name [alter_specification [, alter_specification] ...] [partition_optio ...

  5. MySQL alter table时执行innobackupex全备再看Seconds_Behind_Master

    1.场景描述 早上7:25 接到Report中心同学告警,昨天业务报表数据没有完整跑出来,缺少500位业务员的数据,并且很快定位到,缺少的是huabei_order库上的数据.Report中心的数据是 ...

  6. mysql alter table

    准备: create table t(x int,y int); 用法 1: 修改列的数据类 alter table t modify column y nvarchar(32); 用法2: 给表加一 ...

  7. EDM排版table设置padding在ie7下bug

    今天搞EDM发现一个在ie7下很扯淡的bug,由于以前没遇到过,所以花了点时间来解决下. IE7下bug重现: <table cellpadding="0" cellspac ...

  8. MySQL字符集的设置

    Notice:文章基于ubuntu系统而写 1.关于MySQL字符集 MySQL的字符集支持(Character Set Support)有两个方面: 字符集(Character set)和排序方式( ...

  9. mysql 字符编码设置

    安装mysql时如果字符编码为默认值latin1,则需要修改为utf8以便支持中文数据. 命令如下: 1.显示数据库字符集 mysql> show create database test;+- ...

随机推荐

  1. 【原】ATI显卡设置双显示器

    Ubuntu 12.04系统下加上增加一个显示器后,一直只能镜像显示,或只能用笔记本的屏幕显示,另一个屏幕无法使用了,上网搜索了一下解决办法,通过下面的方法可以解决问题: 编辑/etc/X11/xor ...

  2. EOS 理解

    1.通过石墨烯技术来解决延迟和吞吐量. 2.账户体系:账户是可读的唯一标识符,不是地址.可包含多对公私钥.账户有权限规划.权限有阈值,公私钥有权重,公私钥的权重大于等于阀值才能拥有该权限进行相应操作. ...

  3. linux wheel组

    wheel 组的概念 wheel 组的概念继承自 UNIX.当服务器需要进行一些日常系统管理员无法执行的高级维护时,往往就要用到 root 权限:而“wheel” 组就是一个包含这些特殊权限的用户池: ...

  4. Linux的僵尸进程及其解决方法

    1. 产生原因: 在UNIX 系统中,一个进程结束了,但是他的父进程没有等待(调用wait / waitpid)他,那么他将变成一个僵尸进程.通过ps命令查看其带有defunct的标志.僵尸进程是一个 ...

  5. dom操作------操作元素属性的若干方法

    // 1,通过HTMLElement类型的属性来获得和设置元素特性(设置的是元素属性,比如class,id,title,而不是css样式,比如float,border等)let div = docum ...

  6. 深入出不来nodejs源码-流程总览

    花了差不多两周时间过了下primer C++5th,完成了<C++从入门到精通>.(手动滑稽) 这两天看了下node源码的一些入口方法,其实还是比较懵逼的,语法倒不是难点,主要是大量的宏造 ...

  7. gulpjs

    http://www.cnblogs.com/2050/p/4198792.html   这篇文章很全面的讲解了gulpjs的使用 https://www.jianshu.com/p/9723ca2a ...

  8. LinkedBlockQueue生产消费源码解析

    LinkedBlockQueue自JDK1.5以后提供的一种阻塞队列,遵循生产者消费者模式,实现了BlockQueue接口,如图 从它的名字可以了解到它是采用链表的方式实现了阻塞队列,并且定义了“节点 ...

  9. Jquery/js引入的button的onclik事件只触发一次

    目标描述 我要实现的是:通过监听button的click事件,从而通过ajax向servlet发送请求获取数据库中的数据,然后返回的页面,并要求局部刷新 一次页面的加载是html直接页面初始化本身的 ...

  10. java基础进阶二:HashMap实现原理分析

    HashMap实现原理分析 1. HashMap的数据结构 数据结构中有数组和链表来实现对数据的存储,但这两者基本上是两个极端. 数组 数组存储区间是连续的,占用内存严重,故空间复杂的很大.但数组的二 ...