【问题标题】:Using the now() function and executing triggers使用 now() 函数并执行触发器
【发布时间】:2012-11-25 19:10:51
【问题描述】:

我正在尝试在 PostgreSQL 中创建一个触发器函数,该函数应在插入或更新记录之前检查具有相同 id 的记录(即通过 id 与现有记录进行比较)。如果函数找到具有相同id 的记录,则该条目设置为time_dead。让我用这个例子来解释:

INSERT INTO persons (id, time_create, time_dead, name)
VALUES (1, 'now();', ' ', 'james');

我想要一张这样的桌子:

 id  time_create  time-dead  name
 1   06:12                   henry   
 2   07:12                   muka

id 1 有一个 time_create 06.12,但 time_deadNULL。这与id 2 相同,但下次我尝试使用相同的 id 但不同的名称运行insert 查询时,我应该得到一个这样的表:

 id  time_create  time-dead  name
 1   06:12        14:35      henry   
 2   07:12                   muka
 1   14:35                   waks

henry 和 waks 共享相同的 id 1。运行insert 查询后,henry 的time_dead 等于waks 的time_create。如果要使用 id 1 创建另一个条目,比如说 james,james 的时间条目将等于 waks 的 time_dead。以此类推。

到目前为止,我的功能看起来像这样。但它不起作用:

CREATE FUNCTION tr_function() RETURNS trigger AS '
BEGIN
  IF tg_op = ''UPDATE'' THEN
     UPDATE persons
     SET time_dead = NEW.time_create
     Where
         id = NEW.id
         AND time_dead IS NULL
         ;

  END IF;
  RETURN new;
END
' LANGUAGE plpgsql;

CREATE TRIGGER sofgr BEFORE INSERT OR UPDATE
        ON persons FOR each ROW
        EXECUTE PROCEDURE tr_function();

当我运行它时,它说time_dead 不应该是null。有没有办法我可以编写一个trigger function,它会在inserting or updating 上自动输入时间,但是当我运行select 查询时会给我上表一样的结果?

我做错了什么?

我的两张桌子:

CREATE TABLE temporary_object
(
  id integer NOT NULL,
  time_create timestamp without time zone NOT NULL,
  time_dead timestamp without time zone,
  PRIMARY KEY (id, time_create)
);

CREATE TABLE persons
(
  name text
)
INHERITS (temporary_object);

【问题讨论】:

  • 第一个问题是您不能更新在更新同一个表时触发的触发器内的表。这将导致无限递归。
  • 可以包含表的定义吗?
  • @IgorRomanchenko 等一下
  • @DanielVérité:你当然可以这样做。但是,防止无限递归是您的责任。我添加了一个子句来防止这种情况..
  • 我清理了问题并修复了您设置中的矛盾,以使示例正常工作。

标签: postgresql triggers timestamp infinite-loop plpgsql


【解决方案1】:

触发功能

CREATE FUNCTION tr_function()
  RETURNS trigger AS
$func$
BEGIN

   UPDATE persons p
   SET    time_dead = NEW.time_create
   WHERE  p.id = NEW.id
   AND    p.time_dead IS NULL
   AND    p.name <> NEW.name;

   RETURN NEW;

END
$func$ LANGUAGE plpgsql;
  • 您的触发器函数 (IF tg_op = ''UPDATE'') 中缺少 INSERT 大小写。但是没有必要一开始就检查TG_OP,因为触发器只在INSERT OR UPDATE 上触发——假设您没有在其他触发器中使用相同的功能。所以我去掉了杂物。

  • 请注意,您不必在用美元引用的字符串中转义单引号。

  • 还添加了:

      AND    p.name <> NEW.name
    

... 防止INSERT 立即终止自己(并导致无限递归)。这假设一行永远不能在具有相同 name 的另一行之后。

另外:设置仍然不是防弹的。 UPDATEs 可能会弄乱您的系统。我可以继续更新id 或一行,从而终止其他行但不留下后续行。考虑禁止对id 的更新。当然,这会使触发器ON UPDATE 毫无意义。我怀疑你是否需要开始。


now() 作为默认值

如果您想将now() 用作time_create 的默认值,只需这样做。 Read the manual about setting a column DEFAULT。然后在INSERTs 中跳过time_create 自动填充。

如果您想强制它(防止每个人输入不同的值)创建一个触发器 ON INSERT 或在触发器顶部添加以下内容:

IF TG_OP = 'INSERT' THEN
    NEW.time_create := now();   -- type timestamp or timestamptz!
    RETURN NEW;
END IF;

假设您误导性命名的列“time_create”实际上是timestamp 类型。
这将强制新行的当前时间戳。

【讨论】:

  • 如果将一个时间戳值留空,我的插入查询将不会运行
  • @theuserkaps:你需要比这更具体。如果它不仅仅是一个简单的评论,请编辑您的问题。
  • @theuserkaps:这是persons AS p 的缩写,p 是表别名。寻找“别名”in the manual here
  • ooh ya,另一件事我一直在尝试修改上面的函数,这样当我运行插入查询时,我不应该像现在那样放置 time_create 值,但是当我只插入值时,time_create 应该始终是现在自动使用函数();在函数内部,有什么办法可以做到吗?谢谢
  • 谢谢你真的很有帮助我非常感激我从你那里学到了很多谢谢
猜你喜欢
  • 1970-01-01
  • 2021-10-30
  • 1970-01-01
  • 2011-09-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多