【问题标题】:Trigger function to delete certain rows from the same table触发函数从同一个表中删除某些行
【发布时间】:2014-03-27 19:39:29
【问题描述】:

我正在尝试在 Postgres 中创建一个触发器/函数,该触发器/函数将在插入表时检查是否已经有其他成员发布的具有相同内容的另一个帖子。 如果有帖子,这个函数不会插入新的,保持表格不变。否则,将被添加。

到目前为止,触发器和函数如下所示:

触发器:

CREATE TRIGGER isPostUnique 
AFTER INSERT ON posts 
FOR EACH ROW
EXECUTE PROCEDURE deletePost();

功能:

CREATE FUNCTION deletePost() RETURNS TRIGGER AS $isPostUnique$
BEGIN
IF (EXISTS (SELECT * FROM posts p1, posts p2 
   WHERE (p1.userID <> p2.userID) 
   AND (p1.content LIKE p2.content)))
  THEN
    DELETE FROM NEW WHERE (posts.postID = NEW.postID);
  RETURN NEW;
END IF;
END;
$isPostUnique$ LANGUAGE plpgsql;

添加函数和触发器没有任何错误,但是当我尝试运行以下查询来测试它时:INSERT INTO posts VALUES (7, 3, 'test redundant post', 10, 1); 我收到此错误

ERROR:  relation "new" does not exist
LINE 1: DELETE FROM NEW WHERE (posts.postID = NEW.postID)
                    ^
QUERY:  DELETE FROM NEW WHERE (posts.postID = NEW.postID)
CONTEXT:  PL/pgSQL function dp() line 7 at SQL statement

我知道您不能在 FOR EACH ROW 插入中使用“NEW”,但我不知道如何实现这一点。

【问题讨论】:

  • new 只能在行级 (for each row) 触发器中使用。您究竟希望delete from new 做什么? new 是单行(又名“记录”),不是可以从中删除的表。
  • 我真的是 Postgres 的新手,我只是从文档中尽可能多地尝试,所以我确信其中的很多内容都是没有意义的。我想要完成的是找到与另一个具有相同内容的新插入,并删除刚刚插入的那个。
  • 为了防止插入,只需从触发器返回NULLQuote from the manual: "BEFORE 触发的行级触发器可以返回 null 以指示触发器管理器跳过该行的其余操作(即,不会触发后续触发器,并且不会触发 INSERT/UPDATE/DELETE发生在这一行"
  • 好的,我将触发器中的AFTER INSERT ON 替换为BEFORE INSERT... 并取出THEN DELETE FROM...RETURN NEW 并将THEN 中的内容替换为RETURN NULL。但是,现在我无法在表格中添加任何内容。
  • 有没有我可以编写类似于IF ((NEW.content LIKE posts.content) AND (NEW.userID &lt;&gt; posts.userID)) 的IF 语句而不会出现missing FROM 错误?

标签: sql function postgresql triggers plpgsql


【解决方案1】:

更新问题的更新答案

当然你可以在FOR EACH ROW触发函数中使用NEW。你不能直接在它上面写一个DELETE 声明。它是行类型 (data type HeapTuple to be precise),而不是表格。

如果相同的内容已经存在,中止INSERT静默(不引发异常)...

CREATE FUNCTION deletePost()
  RETURNS TRIGGER AS
$func$
BEGIN

IF EXISTS (
   SELECT 1
   FROM   posts p
   WHERE  p.content = NEW.content
   -- AND p.userID <> NEW.userID  -- I doubt you need this, too?
   ) THEN

    RETURN NULL;  -- cancel INSERT
ELSE
    RETURN NEW;   -- go ahead
END IF;

END
$func$ LANGUAGE plpgsql;

当然这只适用于触发器...

...
BEFORE INSERT ON posts
...

唯一索引

UNIQUE constraint 或唯一索引(几乎相同的效果)可能是更好的解决方案:

CREATE UNIQUE INDEX posts_content_uni_idx (content);

在尝试插入重复值时会引发异常。无需触发器。
它还提供了非常需要的索引来加快速度。

【讨论】:

  • 非常感谢您的帮助,我的工作正常,但倒退了。它是删除原始帖子,而不是新帖子。我有返回“NEW”的函数,因为我不知道如何返回“posts”表而不出现错误。有任何想法吗?这是我的函数现在的样子CREATE FUNCTION deletePost() RETURNS TRIGGER AS $isPostUnique$ BEGIN DELETE FROM posts p WHERE p.userID &lt;&gt; NEW.userID AND p.content LIKE NEW.content; RETURN NEW; END; $isPostUnique$ LANGUAGE plpgsql;
  • @Zach:你的问题并没有给出完整的画面。尝试回答时很糟糕......
  • 抱歉,好像不太清楚。我想要完成的是一个触发器和函数,如果它与另一个用户的帖子匹配,它将不会用新帖子更新“帖子”表。假设用户 ID 0 发布了一个帖子“你好!”并且用户 ID 2 尝试发布“你好!”。我希望该函数意识到 userID 0 已经发布了该帖子,因此不允许将 userID 2 的帖子添加到表中。
  • @Zach:相应地更新了答案。
猜你喜欢
  • 2018-06-29
  • 1970-01-01
  • 2020-09-23
  • 2019-01-07
  • 1970-01-01
  • 1970-01-01
  • 2011-03-17
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多