繁体   English   中英

如果满足条件则阻止插入

[英]Prevent insert if condition is met

我有一个这样的表格Content

id | text | date | idUser → User | contentType 

另一个表Answer

idAnswer → Content | idQuestion → Content | isAccepted

我想确保Answer的日期大于Question的日期。 一个问题是一个ContentcontentType =“问题”。

我尝试使用以下触发器解决此问题,但是当我尝试插入Answer出现错误:

 ERROR: record "new" has no field "idanswer" CONTEXT: SQL statement "SELECT (SELECT "Content".date FROM "Content" WHERE "Content".id = NEW.idAnswer) < (SELECT "Content".date FROM "Content" WHERE "Content".id = NEW.idQuestion)" PL/pgSQL function "check_valid_date_answer" line 2 at IF

扳机:

CREATE TRIGGER check_valid_answer 
AFTER INSERT ON "Answer"
FOR EACH ROW EXECUTE PROCEDURE check_valid_date_answer();

触发功能:

CREATE FUNCTION check_valid_date_answer() RETURNS trigger
    LANGUAGE plpgsql
    AS $$BEGIN
  IF (SELECT "Content".date FROM "Content"
      WHERE "Content".id = NEW.idAnswer)
   < (SELECT "Content".date FROM "Content"
      WHERE "Content".id = NEW.idQuestion) 
  THEN
    RAISE NOTICE 'This Answer is an invalid date';
  END IF;
  RETURN NEW;
END;$$;

所以,我的问题是:我真的需要为此创建触发器吗? 我看到我不能在Answer使用CHECK ,因为我需要与另一个表的属性进行比较。 有没有其他(更简单/更好)的方法来做到这一点? 如果没有,为什么会出现错误,我该如何解决?

你的基本方法是合理的。 触发器是一个有效的解决方案。 除了3 个问题之外,它应该可以工作:

1)您的命名约定

我们需要查看您的确切表定义才能确定,但​​证据就在那里。 错误消息说: has no field "idanswer" - 小写。 不说"idAnswer" - 骆驼案例。 如果您在 Postgres 中创建了 CaMeL 案例标识符,那么您将在他们的余生中到处双引号。

2) 中止违规插入

  • 要么引发EXCEPTION而不是友好的NOTICE来实际中止整个事务。

  • 或者RETURN NULL而不是RETURN NEW以静默方式中止插入的行而不引发异常并且不回滚任何内容。

我会做第一个。 这可能会修复手头的错误并起作用:

CREATE FUNCTION trg_answer_insbef_check()
  RETURNS trigger AS
$func$
BEGIN
   IF (SELECT c.date FROM "Content" c WHERE c.id = NEW."idAnswer")
    < (SELECT c.date FROM "Content" c WHERE c.id = NEW."idQuestion") THEN
      RAISE EXCEPTION 'This Answer is an invalid date';
   END IF;
   RETURN NEW;
END
$func$  LANGUAGE plpgsql;

正确的解决方案是只使用合法的小写名称并完全避免此类问题。 这包括您不幸的表名以及列名date ,它是标准 SQL 中的保留字,不应用作标识符 - 即使 Postgres 允许。

3) 应该是BEFORE触发器

CREATE TRIGGER insbef_check
BEFORE INSERT ON "Answer"
FOR EACH ROW EXECUTE PROCEDURE trg_answer_insbef_check();

您想在执行任何其他操作之前中止无效插入。

当然,您必须确保时间戳表Content不能更改,或者您需要更多触发器来确保满足您的条件。
Answer的 fk 列也是如此。

我会以不同的方式解决这个问题。

推荐:

  • 如果要在插入数据之前更改数据,请使用 BEFORE INSERT 触发器
  • 如果您必须做额外的工作,请使用 AFTER INSERT 触发器
  • 如果您有其他数据一致性要求,请使用 CHECK 子句。

所以写一个sql函数来检查一个日期早于另一个的条件,并添加检查约束。 是的,您可以从函数中的其他表中进行选择。

在 SO 上写了一些类似的(复杂检查) 来回答这个问题

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM