如何在 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在每次修改行操作时自动将列更新为当前时间戳?