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

原创 2014年09月01日 16:29:59
在写这篇文章之前,明确我的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日

版权声明:本文为博主原创文章,未经博主允许不得转载。

mysql创建与更新时间字段

mysql创建与更新时间字段
  • jacke121
  • jacke121
  • 2017年02月02日 13:08
  • 3037

mysql性能优化(五) mysql中SELECT+UPDATE处理并发更新问题解决方案

一 问题背景: 假设MySQL数据库有一张会员表vip_member(InnoDB表),结构如下: 当一个会员想续买会员(只能续买1个月、3个月或6个月)时,必须满足以...
  • zengdeqing2012
  • zengdeqing2012
  • 2015年05月13日 16:01
  • 4052

mysql设置datetime默认值为now(但不支持)

from  http://www.cnblogs.com/panxuejun/p/6137698.html MYSQL-TIMESTAMP数据类型的默认值与自动更新问题 在...
  • u010820135
  • u010820135
  • 2017年06月22日 15:45
  • 1213

Mysql中created_at字段与updated_at字段关于自动更新与自动插入时间戳

Macall
  • macallbobo
  • macallbobo
  • 2015年04月21日 16:03
  • 1143

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

MySQL中同时存在创建和上次更新时间戳字段解决方法浅析。
  • Wentasy
  • Wentasy
  • 2014年09月01日 16:29
  • 6325

Mysql竟然不支持同一个表的关联查询

  晕!Mysql竟然不支持将同一个表用不同的别名进行关联查询,甚至不能在一条sql语句中两次使用同一个表。错误提示: #1137 - Cant reopen table: mytablecreate...
  • flynetcn
  • flynetcn
  • 2007年11月09日 14:13
  • 2602

MySQL中同时存在创建和上次更新时间戳字段解决方法浅析;MYSQL日期 字符串 时间戳互转

mysql时间戳,时间格式转换
  • just3do
  • just3do
  • 2017年03月12日 15:47
  • 209

mysql插入数据时保存当前时间戳/更新数据数修改时间戳

timestamp有两个属性,分别是CURRENT_TIMESTAMP 和ON UPDATE CURRENT_TIMESTAMP两种,使用情况分别如下: 1. CURRENT_TI...
  • huanleyan
  • huanleyan
  • 2014年08月27日 17:58
  • 3972

mysql5.5不能同时设置两个字段为CURRENT_TIMESTAMP

mysql5.6不存在此问题,mysql5.6重新定义了TIMESTAMP : Previously, at most one TIMESTAMP column per table could ...
  • heweimingming
  • heweimingming
  • 2016年05月04日 16:23
  • 1725

mysql不能在更新一个表的同时查询同样的一张表

记得在看高性能mysql的时候,说过mysql不能在更新一个表的同时查询同一张表,今天看了一个stackoverflow的求助帖,突然间想起了这件事情。 http://stackoverflow.co...
  • whodarewin2005
  • whodarewin2005
  • 2014年01月12日 14:48
  • 1254
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:MySQL中同时存在创建和上次更新时间戳字段解决方法浅析
举报原因:
原因补充:

(最多只允许输入30个字)