如何在 PostgreSQL 中自动更新时间戳

Posted

技术标签:

【中文标题】如何在 PostgreSQL 中自动更新时间戳【英文标题】:How do I automatically update a timestamp in PostgreSQL 【发布时间】:2012-03-22 07:47:05 【问题描述】:

我希望代码能够在插入新行时自动更新时间戳,就像我在 mysql 中使用 CURRENT_TIMESTAMP 所做的那样。

如何在 PostgreSQL 中实现这一点?

CREATE TABLE users (
    id serial not null,
    firstname varchar(100),
    middlename varchar(100),
    lastname varchar(100),
    email varchar(200),
    timestamp timestamp
)

【问题讨论】:

顺便说一下,你的timestamp数据类型是由SQL规范定义为TIMESTAMP WITHOUT TIME ZONE的缩写。这几乎肯定不是你想要的,就像explained by Postgres expert David E. Wheeler。另一种类型,TIMESTAMP WITH TIME ZONE 可能是您想要的,使用任何传递的时区偏移信息将日期时间调整为 UTC(但实际上并没有存储该时区信息,尽管类型名称)。 在重新学习了这一点之后,我写了一个detailed blog post,关于使用默认值、函数和触发器记录行的创建和修改的日期时间。包括用于 Postgres 的完整示例 SQL 和 PL/pgSQL 代码。 【参考方案1】:

要在插入期间填充列,请使用 DEFAULT 值:

CREATE TABLE users (
  id serial not null,
  firstname varchar(100),
  middlename varchar(100),
  lastname varchar(100),
  email varchar(200),
  timestamp timestamp default current_timestamp
)

请注意,该列的值可以通过在INSERT 语句中提供值来显式覆盖。如果您想防止这种情况发生,您确实需要trigger。

如果您需要在更新行时更新该列(如mentioned by E.J. Brennan),则还需要一个触发器

请注意,对列名使用保留字通常不是一个好主意。您应该找到与 timestamp 不同的名称

【讨论】:

请注意 Postgres 有 functions 告诉您 (1) 实际当前时刻的时间,(2) 语句开始的时间,以及 (3) 事务开始的时间。此处显示的示例是当前事务的开始。与其他两种可能性相反,您可能想要也可能不想要。 避免名称与保留字冲突的好处。请注意,SQL 规范明确承诺永远不会使用结尾的下划线作为保留字。所以你可以把timestamp变成timestamp_。更好的是一个更具描述性的名称,例如row_created_ 前半部分根本不是OP所要求的,尽管后半部分提到了其他答案(这是正确的),但它仍然具有误导性。这不应该被接受为答案。 在类型的 Postgres 文档中这样说。 Varchar 有额外的 CPU 周期来检查约束,这在 TEXT 上不会发生。 没有单个字段,但我从未见过有单个字段的有用表格。该约束因字段数量而更加复杂。我记得将表格中的所有 varchar 更改为文本并获得了 15% 的写作改进。此外,小的性能损失不是“否”性能损失。【参考方案2】:

您需要编写一个插入触发器,如果​​您希望在更改记录时更改它,还可能编写一个更新触发器。这篇文章解释得很好:

http://www.revsys.com/blog/2006/aug/04/automatically-updating-a-timestamp-column-in-postgresql/

CREATE OR REPLACE FUNCTION update_modified_column()   
RETURNS TRIGGER AS $$
BEGIN
    NEW.modified = now();
    RETURN NEW;   
END;
$$ language 'plpgsql';

像这样应用触发器:

CREATE TRIGGER update_customer_modtime BEFORE UPDATE ON customer FOR EACH ROW EXECUTE PROCEDURE  update_modified_column();

【讨论】:

这是一个更完整的解决方案,适用于初始 INSERT 以及任何后续 UPDATE(有利于数据审计)。当然,OP 只要求前者。【参考方案3】:

更新时间戳,仅当值发生变化时

基于 E.J 的链接并从此链接添加 if 语句 (https://***.com/a/3084254/1526023)

CREATE OR REPLACE FUNCTION update_modified_column()
RETURNS TRIGGER AS $$
BEGIN
   IF row(NEW.*) IS DISTINCT FROM row(OLD.*) THEN
      NEW.modified = now(); 
      RETURN NEW;
   ELSE
      RETURN OLD;
   END IF;
END;
$$ language 'plpgsql';

【讨论】:

【参考方案4】:

使用 'now()' 作为默认值会自动生成时间戳。

【讨论】:

仅用于添加值,更新记录时似乎不使用默认值。【参考方案5】:

要在插入新行时自动更新 PostgresSQL 中的时间戳字段,您可以将 current_timestamp 设置为其 default 值:

CREATE TABLE users (
    id serial not null,
    firstname varchar(100),
    middlename varchar(100),
    lastname varchar(100),
    email varchar(200),
    timestamp timestamp default current_timestamp
)

除此之外,您可能希望阻止任何人在将来更新此字段,这可以通过创建更新触发器并应用它来完成:

CREATE OR REPLACE FUNCTION stop_change_on_timestamp()
  RETURNS trigger AS
$BODY$
BEGIN
  -- always reset the timestamp to the old value ("actual creation time")
  NEW.timestamp := OLD.timestamp;
  RETURN NEW;
END;
$BODY$
CREATE TRIGGER prevent_timestamp_changes
  BEFORE UPDATE
  ON users
  FOR EACH ROW
  EXECUTE PROCEDURE stop_change_on_timestamp();

【讨论】:

【参考方案6】:

对于更新和 Liquibase YAML:

databaseChangeLog:
  - changeSet:
      id: CREATE FUNCTION set_now_to_timestamp
      author: Konstantin Chvilyov
      changes:
        - sql:
            sql: CREATE OR REPLACE FUNCTION set_now_to_timestamp() RETURNS TRIGGER LANGUAGE plpgsql AS 'BEGIN NEW.timestamp = NOW(); RETURN NEW; END;'

  - changeSet:
      id: CREATE TRIGGER update_users_set_now_to_timestamp
      author: Konstantin Chvilyov
      changes:
        - sql:
            sql: CREATE TRIGGER update_users_set_now_to_timestamp BEFORE UPDATE ON users FOR EACH ROW EXECUTE PROCEDURE set_now_to_timestamp();

【讨论】:

以上是关于如何在 PostgreSQL 中自动更新时间戳的主要内容,如果未能解决你的问题,请参考以下文章

如何使mysql在每次修改行操作时自动将列更新为当前时间戳?

如何在 SQLite 中设置自动时间戳?

时间戳(自动)何时更新?

在 PostgreSQL 中更新行时更新时间戳

有没有办法设置“过期”时间,之后在 PostgreSQL 中自动删除数据条目?

自动更新 NSManagedObject 属性修改时间戳