MySQL中同时存在创建和上次更新时间戳字段解决方法浅析

在写这篇文章之前,明确我的MySQL版本。

mysql> SELECT VERSION();
+------------+
| VERSION()  |
+------------+
| 5.5.29-log |
+------------+
1 row in set (0.00 sec)

现在有这样的需求,一张表中有一个字段created_at记录创建该条记录的时间戳,另一个字段updated_at记录更新该条记录的时间戳。
我们尝试以下几个语句。

第一个,测试通过。

CREATE TABLE temp
(
	id INT(11) PRIMARY KEY AUTO_INCREMENT,
	name VARCHAR(10),
	updated_at timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);

第二个,测试不通过。报ERROR 1293 (HY000)错误。(完整错误信息:ERROR 1293 (HY000): Incorrect table definition; there can be only one TIMESTAMP column with CURRENT_TIMESTAMP in DEFAULT or ON UPDATE clause)

CREATE TABLE temp
(
	id INT(11) PRIMARY KEY AUTO_INCREMENT,
	name VARCHAR(10),
	created_at timestamp NULL DEFAULT CURRENT_TIMESTAMP,
	updated_at timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);

MySQL 5.5.29中有这样的奇葩限制,不明白为什么。既然有这样的限制,那么只有绕道而行,现在尝试给出如下几种解决办法。

第一种,created_at使用DEFAULT CURRENT_TIMESTAMP或者DEFAULT now(),updated_at使用触发器。
具体解决方法如下:
1.temp表结构如下:

CREATE TABLE temp
(
	id INT(11) PRIMARY KEY AUTO_INCREMENT,
	name VARCHAR(10),
	created_at timestamp NULL DEFAULT CURRENT_TIMESTAMP,
	updated_at timestamp NULL
);

2.插入测试数据:

mysql> INSERT INTO temp(name,created_at,updated_at) VALUES(‘robin‘,now(),now());
Query OK, 1 row affected (0.03 sec)

mysql> INSERT INTO temp(name,created_at,updated_at) VALUES(‘wentasy‘,now(),now());
Query OK, 1 row affected (0.01 sec)

mysql> SELECT * FROM temp;
+----+---------+---------------------+---------------------+
| id | name    | created_at          | updated_at          |
+----+---------+---------------------+---------------------+
|  1 | robin   | 2014-09-01 14:00:39 | 2014-09-01 14:00:39 |
|  2 | wentasy | 2014-09-01 14:01:11 | 2014-09-01 14:01:11 |
+----+---------+---------------------+---------------------+
2 rows in set (0.00 sec)

3.在temp上创建触发器,实现更新时记录更新时间;

delimiter |
DROP TRIGGER IF EXISTS tri_temp_updated_at;
CREATE TRIGGER tri_temp_updated_at BEFORE UPDATE ON temp
FOR EACH ROW
BEGIN
	SET NEW.updated_at = now();
END;
|
delimiter ;

4.测试。

mysql> UPDATE temp SET name=‘robinwen‘ WHERE id=1;
Query OK, 1 row affected (0.01 sec)
Rows matched: 1  Changed: 1  Warnings: 0

#可以看到已经记录了第一条数据的更新时间
mysql> SELECT * FROM temp;
+----+----------+---------------------+---------------------+
| id | name     | created_at          | updated_at          |
+----+----------+---------------------+---------------------+
|  1 | robinwen | 2014-09-01 14:00:39 | 2014-09-01 14:03:05 |
|  2 | wentasy  | 2014-09-01 14:01:11 | 2014-09-01 14:01:11 |
+----+----------+---------------------+---------------------+
2 rows in set (0.00 sec)

第二种,created_at使用触发器,updated_at使用DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP或者DEFAULT now() ON UPDATE now();

具体解决方法如下:
1.temp表结构如下:

CREATE TABLE temp
(
	id INT(11) PRIMARY KEY AUTO_INCREMENT,
	name VARCHAR(10),
	created_at timestamp NULL,
	updated_at timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);

2.在temp上创建触发器,实现插入数据记录创建时间;

delimiter |
DROP TRIGGER IF EXISTS tri_temp_created_at;
CREATE TRIGGER tri_temp_created_at BEFORE INSERT ON temp
FOR EACH ROW
BEGIN
	IF new.created_at IS NULL
	THEN
		SET new.created_at=now();
	END IF;
END;
|
delimiter ;

3.插入测试数据:

mysql> INSERT INTO temp(name,created_at,updated_at) VALUES(‘robin‘,now(),now());
Query OK, 1 row affected (0.01 sec)

mysql> INSERT INTO temp(name,created_at,updated_at) VALUES(‘wentasy‘,now(),now());
Query OK, 1 row affected (0.01 sec)

mysql> SELECT * FROM temp;
+----+---------+---------------------+---------------------+
| id | name    | created_at          | updated_at          |
+----+---------+---------------------+---------------------+
|  1 | robin   | 2014-09-01 14:08:36 | 2014-09-01 14:08:36 |
|  2 | wentasy | 2014-09-01 14:08:44 | 2014-09-01 14:08:44 |
+----+---------+---------------------+---------------------+
2 rows in set (0.00 sec)

4.测试。

mysql> UPDATE temp SET name=‘robinwen‘ WHERE id=1;
Query OK, 1 row affected (0.01 sec)
Rows matched: 1  Changed: 1  Warnings: 0

#可以看到已经记录了第一条数据的更新时间
mysql> SELECT * FROM temp;
+----+----------+---------------------+---------------------+
| id | name     | created_at          | updated_at          |
+----+----------+---------------------+---------------------+
|  1 | robinwen | 2014-09-01 14:08:36 | 2014-09-01 14:09:09 |
|  2 | wentasy  | 2014-09-01 14:08:44 | 2014-09-01 14:08:44 |
+----+----------+---------------------+---------------------+
2 rows in set (0.00 sec)

第三种,created_at指定timestamp DEFAULT ‘0000-00-00 00:00:00‘,updated_at指定DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP或者timestamp DEFAULT now() ON UPDATE now();
具体解决方法如下:
1.temp表结构如下:

CREATE TABLE temp
(
	id INT(11) PRIMARY KEY AUTO_INCREMENT,
	name VARCHAR(10),
	created_at timestamp NULL DEFAULT ‘0000-00-00 00:00:00‘,
	updated_at timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);

2.插入测试数据:

mysql> INSERT INTO temp(name,created_at,updated_at) VALUES(‘robin‘,now(),now());
Query OK, 1 row affected (0.01 sec)

mysql> INSERT INTO temp(name,created_at,updated_at) VALUES(‘wentasy‘,now(),now());
Query OK, 1 row affected (0.01 sec)

mysql> SELECT * FROM temp;
+----+---------+---------------------+---------------------+
| id | name    | created_at          | updated_at          |
+----+---------+---------------------+---------------------+
|  1 | robin   | 2014-09-01 14:10:43 | 2014-09-01 14:10:43 |
|  2 | wentasy | 2014-09-01 14:10:57 | 2014-09-01 14:10:57 |
+----+---------+---------------------+---------------------+
2 rows in set (0.00 sec)

3.测试。

mysql> UPDATE temp SET name=‘robinwen‘ WHERE id=1;
Query OK, 1 row affected (0.01 sec)
Rows matched: 1  Changed: 1  Warnings: 0

#可以看到已经记录了第一条数据的更新时间
mysql> SELECT * FROM temp;
+----+----------+---------------------+---------------------+
| id | name     | created_at          | updated_at          |
+----+----------+---------------------+---------------------+
|  1 | robinwen | 2014-09-01 14:10:43 | 2014-09-01 14:11:24 |
|  2 | wentasy  | 2014-09-01 14:10:57 | 2014-09-01 14:10:57 |
+----+----------+---------------------+---------------------+
2 rows in set (0.00 sec)

第四种,更换MySQL版本,MySQL 5.6已经去除了此限制。

我们可以看下MySQL 5.5和5.6帮助文档对于这个问题的解释。

From the MySQL 5.5 documentation:

One TIMESTAMP column in a table can have the current timestamp as the default value for initializing the column, as the auto-update value, or both. It is not possible to have the current timestamp be the default value for one column and the auto-update value for another column.

Changes in MySQL 5.6.5:

Previously, at most one TIMESTAMP column per table could be automatically initialized or updated to the current date and time. This restriction has been lifted. Any TIMESTAMP column definition can have any combination of DEFAULT CURRENT_TIMESTAMP and ON UPDATE CURRENT_TIMESTAMP clauses. In addition, these clauses now can be used with DATETIME column definitions. For more information, see Automatic Initialization and Updating for TIMESTAMP and DATETIME.

我们确定下MySQL的版本。

mysql> SELECT VERSION();
+---------------------------------------+
| VERSION()                             |
+---------------------------------------+
| 5.6.20-enterprise-commercial-advanced |
+---------------------------------------+
1 row in set (0.00 sec)

我们把文首测试不通过的SQL语句在MySQL 5.6下执行,可以看到没有任何错误。

CREATE TABLE temp
(
	id INT(11) PRIMARY KEY AUTO_INCREMENT,
	name VARCHAR(10),
	created_at timestamp NULL DEFAULT CURRENT_TIMESTAMP,
	updated_at timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
Query OK, 0 rows affected (0.28 sec)

接着我们插入测试语句,并作测试。

mysql> INSERT INTO temp(name) VALUES(‘robin‘);
Query OK, 1 row affected (0.07 sec)

mysql> INSERT INTO temp(name) VALUES(‘wentasy‘);
Query OK, 1 row affected (0.00 sec)

mysql> SELECT * FROM temp;
+----+---------+---------------------+---------------------+
| id | name    | created_at          | updated_at          |
+----+---------+---------------------+---------------------+
|  1 | robin   | 2014-09-01 15:05:57 | 2014-09-01 15:05:57 |
|  2 | wentasy | 2014-09-01 15:06:02 | 2014-09-01 15:06:02 |
+----+---------+---------------------+---------------------+
2 rows in set (0.01 sec)

mysql> UPDATE temp SET name=‘robinwen‘ WHERE id=1;
Query OK, 1 row affected (0.02 sec)
Rows matched: 1  Changed: 1  Warnings: 0

#可以看到已经记录了第一条数据的更新时间
mysql> SELECT * FROM temp;
+----+----------+---------------------+---------------------+
| id | name     | created_at          | updated_at          |
+----+----------+---------------------+---------------------+
|  1 | robinwen | 2014-09-01 15:05:57 | 2014-09-01 15:06:45 |
|  2 | wentasy  | 2014-09-01 15:06:02 | 2014-09-01 15:06:02 |
+----+----------+---------------------+---------------------+
2 rows in set (0.00 sec)

总结
本文介绍的方法归根结底,就两条,一是建表语句指定默认值和更新动作,二是使用触发器插入默认值和更新时间。面对当前无法更改的事实,只能采取折中的办法或者牺牲更多来弥补。还有一条值得注意的是,遇到问题多想想不同的解决办法,尽可能地列出所有可能或者可行的方案,这样一来让自己学到更多,二来可以锻炼思维的广度,三来多种方案可以弥补某种方案在特定环境下不可行的不足。

Good Luck!

Robin

2014年9月1日

时间: 2024-10-11 23:18:43

MySQL中同时存在创建和上次更新时间戳字段解决方法浅析的相关文章

mysql中timestamp的自动生成与更新

转自:mysql中timestamp的自动生成与更新 MYSQL中TIMESTAMP类型可以设定默认值,就像其他类型一样.1.自动UPDATE 和INSERT 到当前的时间:表:---------------------------------Table Create Table ------ -------------------------- CREATE TABLE `t1` ( `p_c` int(11) NOT NULL, `p_time` timestamp NOT NULL DEF

pentaho中Invalid byte 3 of 3-byte UTF-8 sequence的解决方法

今天在创建数据源的时候报了这么一个错 Invalid byte 3 of 3-byte UTF-8 sequence. 上网一查,说是有中文,字体不相符,可是我没有写中文,为了能继续往后进行,修改了如下文件 在start-pentaho.bat中添加了一句代码 修改前 IF "%BITS%" == "64" ( set CATALINA_OPTS=-Xms1024m -Xmx2048m -XX:MaxPermSize=256m -Dsun.rmi.dgc.clien

【第一个项目】MySQL第二天早上第一次连接超时报错,解决方法com.mysql.jdbc.exceptions.jdbc4.CommunicationsException:

MySQL第二天早上第一次连接超时报错,解决方法com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: Communications link failure com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: Communications link failure Last packet sent to the server was 0 ms ago 最近碰到一个mysql5

Eclipse中输入变量会自动补全上屏的解决方法

我自己在启动Eclipse代码补全后输入感觉确实爽多了,但是每次输入变量后一按下空格,编译器会自己帮你写一个很蛋疼的名字,比如你输入了:String mStr后按下空格,它就变成了mString,十分讨厌.下面是正规的解决办法,但是经过我反复折腾,我实在没办法把插件的源码显示到工程中.于是偷个懒去网上下载了已经修改好的jar包,成功搞定. 我自己复制jar包的目录是:D:\Work Appliactions\adt-bundle-windows-x86-20140702\eclipse\plug

VMware ESXi中不能显示CPU及内存使用情况的解决方法

今天一个网友问我,他管理的机房有4台ESXi 5.1的服务器,其中三台ESXi Server不能显示各个虚拟机占用的CPU.内存情况了,如图1-1所示. 图1-1在"虚拟机"选项卡中不能显示每个启动虚拟机的资源占用情况 另外,在VMware ESXi的"摘要"中,CPU与内存的使用情况也统计出错,如图1-2所示. 图1-2 摘要统计出错 在出现这个问题时,各个ESXi Server上的虚拟机可以正常启动.关闭,并且各虚拟机运行的系统及应用不受影响. 从上面两个图我看

Mysql:Forcing close of thread xxx user: 'root' 的解决方法

MySQL server在中午的时候忽然挂掉.重启mysql也尽是失败,只有重启电脑才能解决,然而重装了MySQL也是不行,晚上还是挂, 去看mysql的errorlog,只能看到类似如下的信息: Forcing close of thread xxxxx user: 'root' 百度之后 发现这算属MySQL的一个bug,不管连接是通过hosts还是ip的方式,MySQL都会对DNS做反查,IP到DNS,由于反查的接续速度过慢(不管是不是isp提供的dns服务器的问题或者其他原因),大量的查

打开SDK Manager检查Android SDK下载和更新失败的解决方法

[故障描述] 打开SDK Manager检查Android  SDK状况,出现以下情况: Failed to fetch URL https://dl-ssl.google.com/android/repository/addons_list-2.xml, reason: Connection to https://dl-ssl.google.com refused Failed to fetch URL https://dl-ssl.google.com/android/repository/

android studio 更新 Gradle错误解决方法(Gradle sync failed)

android studio 更新 Gradle错误解决方法 Android Studio每次更新版本都会更新Gradle这个插件,但由于长城的问题每次更新都是失败,又是停止在Refreshing Gradle Project ,有时新建项目的时候报 Gradle Project Compile Error 等等相关的问题 解决这些问题办法是 首先打开android studio项目 找到项目目录gradle\wrapper\gradle-wrapper.properties这个文件 内容如下

golang github.com/go-sql-driver/mysql 遇到的数据库,设置库设计不合理的解决方法

golang github.com/go-sql-driver/mysql 遇到的数据库,设置库设计不合理的解决方法,查询中报了以下这个错 Scan error on column index 2: unsupported Scan, storing driver.Value type <nil> 解决方案就是动态的把数据 字段前加一上一个COALESCE SELECT u.id,ta.`title` as `活动名` ,COALESCE(IFNULL(i.name,i.nickname) F