【问题标题】:Two MySQL timestamp columns in one table一张表中有两个 MySQL 时间戳列
【发布时间】:2011-09-28 10:50:52
【问题描述】:

我想创建一个表,其中既有“created”列,又有“updated”列。 “创建”列将在插入时设置并且永远不会更改。每次更新行时,“已更新”列都会更改。我不想在随后的 INSERT 或 UPDATE 语句中弄乱这些列中的任何一个。那么,如果我以这样的方式开始,我的 CREATE TABLE 语句应该是什么样的呢?

CREATE TABLE IF NOT EXISTS `mydb`.`mytable` (
  `id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
  `updated` TIMESTAMP,
  `created` TIMESTAMP,
  `deleted` TINYINT DEFAULT 0,
  `notes` TEXT DEFAULT '',
  `description` VARCHAR(100)
) TYPE=innodb;

我似乎在创建包含两个 TIMESTAMP 列的表时遇到问题。我不在乎这些列是 TIMESTAMP 还是 DATETIME 或其他什么,我只是希望它们由 MySQL 填充,而不需要来自 insert 或 update 语句的显式指令。

我希望能够像这样插入:

INSERT INTO `mydb`.`mytable` (notes,description) VALUES ('some note','some description');

还有这样的更新:

UPDATE `mydb`.`mytable` SET notes=CONCAT(notes,'some more notes') WHERE id=1;

两者都无需在插入或更新语句中显式设置“已创建”列或设置(或重置)“更新”列。

【问题讨论】:

  • 每次发生更新时,您都需要一个 ON UPDATE 触发器来更新“已更新”列。我会用一个完整的例子来回答,但我不知道这在 MySQL 中是如何完成的,因为这些天我只使用 PostgreSQL。但我确信 MySQL 手册会为您提供完整的语法。

标签: mysql


【解决方案1】:

另一种方法是改变时间戳列的顺序

像这样设置第一列的默认值

ALTER TABLE `tblname` CHANGE `first_timestamp_column` 
     `first_timestamp_column` TIMESTAMP NOT NULL DEFAULT 0;

Reference

【讨论】:

    【解决方案2】:

    新闻快讯:在 mysql 中,每次更新行中的任何其他列时,TIMESTAMP 列总是更新为 now() - 这是这种数据类型的特意设计的特性。

    另一方面,DATETIME 没有这种奇怪的行为 - 这是完全正常的。

    答案:created必须是DATETIME,但是由于this bug,你还需要一个触发器,像这样:

    CREATE TABLE IF NOT EXISTS mytable (
      `id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
      `updated` TIMESTAMP, -- This will be updated to now(), if you don't set it or set it to null
      `created` DATETIME NOT NULL, -- This will never be magically updated once written
      `deleted` TINYINT DEFAULT 0,
      `notes` TEXT DEFAULT '',
      `description` VARCHAR(100)
    ) TYPE=innodb;
    
    DELIMITER ~
    CREATE TRIGGER mytable_insert_trigger
    BEFORE INSERT ON mytable
    FOR EACH ROW BEGIN
        SET NEW.created = CURRENT_TIMESTAMP;
    END;~
    DELIMITER ;
    
    insert into mytable (notes) values ('test');
    select * from mytable;
    +----+---------------------+---------------------+---------+-------+-------------+
    | id | updated             | created             | deleted | notes | description |
    +----+---------------------+---------------------+---------+-------+-------------+
    |  1 | 2011-07-05 11:48:02 | 2011-07-05 11:48:02 |       0 | test  | NULL        |
    +----+---------------------+---------------------+---------+-------+-------------+
    

    【讨论】:

    • 正如有一种很好的方法可以在每次更新时自动重置 updated,问题是如何在插入时设置 created 而无需将其显式添加到每个插入语句中。
    • 如上面@Dark Slipstream 的回复中所述,NOW() 不适用于 DATETIME。
    • “TIMESTAMP 列总是用 now() 每次更新行中的任何其他列时更新”-> 这应该在 MySQL 的文档中强调,直到我读到这个我不明白为什么所有表中的时间戳在更新时更新为相同的值,谢谢!
    • 在 mysql 版本 5.5 和 5.7 TIMESTAMP 列仅在其默认值设置为 CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP 时才使用 now() 更新,否则更新不同列时值保持不变。此外,NOW() 适用于 5.5 和 5.7 中的 DATETIME 列
    【解决方案3】:

    试试这个来创建你的表:

    CREATE TABLE IF NOT EXISTS db.test_table
    (
    Id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
    created DATETIME DEFAULT NULL,
    updated TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    deleted TINYINT DEFAULT 0,
    notes TEXT DEFAULT NULL,
    description VARCHAR(100)
    )
    

    注意

    updated TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
    

    将允许自动更新此字段。

    并在插入记录之前将此设置为触发器:

    DELIMITER $$
    
    CREATE
        /*[DEFINER = { user | CURRENT_USER }]*/
        TRIGGER `db`.`on_before_insert` BEFORE INSERT
        ON `db`.`test_table`
        FOR EACH ROW BEGIN
        SET new.created = NOW();    
        END$$
    
    DELIMITER ;
    

    那你就可以用这个来插入了:

    INSERT INTO db.test_table(description) VALUES ("Description")
    

    并更新您的记录

    UPDATE db.test_table SET description = "Description 2" where Id=1
    

    您创建和更新的字段将被适当设置。

    【讨论】:

    • 您将赢得对所提问题的最明确回答。不过,@Dark Slipstream 的回答也有很多内容。
    • 我认为时间戳应该用于创建和更新。使用 mysql 5.5 版,您可以将创建的时间戳设置为默认为 current_timestamp,您可以将更新的时间戳设置为在更新时自动更新并将默认设置为当前时间戳。并且不需要触发器。
    【解决方案4】:

    试试这个:

    CREATE TABLE IF NOT EXISTS mydb.mytable
    (
        id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
        updated DATETIME,
        created TIMESTAMP,
        deleted TINYINT DEFAULT 0,
        notes TEXT DEFAULT '',
        description VARCHAR(100)
    ) TYPE=innodb;
    

    编辑:使用触发器。

    CREATE TRIGGER mytable_update
    BEFORE UPDATE ON mydb.mytable
        FOR EACH ROW SET NEW.updated = NOW();
    

    【讨论】:

    • 我不认为 DATETIME 可以是 DEFAULT NOW()
    • @Dark Slipstream,在插入之后但在任何更新之前,该行会是什么样子? mydb.mytable.updated 有什么价值吗?我们是否还需要一个触发器来设置初始值?
    • 如果您使用插入查询,只需在查询中使用 NOW()。在您的更新查询中,您甚至不会提及更新。
    • @Dark Slipstream,我的问题的重点是在创建表后不必在任何查询中解决createdupdated。触发器也很酷,只要我们不必再考虑它。
    • created 将在创建记录时被赋予 CURRENT_TIMESTAMP 值。如果您使用触发器,那么 update 只会在每次更新期间被赋予一个值。
    【解决方案5】:

    不幸的是,MySQL 不允许您在一个表中有两个 TIMESTAMP 列。我会使用 ON UPDATE CURRENT_TIMESTAMP 作为更新的列,并使用 NOW() 函数手动创建。

    【讨论】:

    • 正如我的问题中所述,手动设置“创建”是我明确希望避免的。
    • 老实说,我希望我错了,并且有更好的方法! :)
    • Andreas Jansson,尽管它不支持单独的表定义,但这仍然很容易实现。请参阅 james_bond 的回答。
    • @macek,太好了!绝对是时候阅读触发器了。
    • 可以有多个TIMESTAMP字段stackoverflow.com/questions/4851672/…
    猜你喜欢
    • 1970-01-01
    • 2013-03-02
    • 2016-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-03-05
    相关资源
    最近更新 更多