sql - 如果满足条件则阻止插入

标签 sql postgresql database-design triggers constraints

我有一个表 Content 是这样的:

id | text | date | idUser → User | contentType 

还有另一个表答案:

idAnswer → Content | idQuestion → Content | isAccepted

我想确保Answer 的日期大于Question 的日期。问题是 ContentcontentType = 'QUESTION'。

我尝试使用以下触发器解决此问题,但是当我尝试插入 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) 您的命名约定:

我们需要查看您的确切表定义才能确定,但​​证据就在那里。错误消息显示:没有字段 "idanswer" - 小写。不说 "idAnswer" - CaMeL 大小写。如果您在 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.<b>"</b>idAnswer<b>"</b>)
    < (SELECT c.date FROM "Content" c WHERE c.id = NEW.<b>"</b>idQuestion<b>"</b>) THEN
      RAISE <b>EXCEPTION</b> 'This Answer is an invalid date';
   END IF;
   RETURN NEW;
END
$func$  LANGUAGE plpgsql;

正确的解决方案是使用legal, lower case names完全避免此类问题。这包括您不幸的表名以及列名 date,这是一个 reserved word在标准 SQL 中,不应用作标识符 - 即使 Postgres 允许。

3) 应该是 BEFORE trigger

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

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

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

关于sql - 如果满足条件则阻止插入,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22733254/

相关文章:

postgresql - Docker Postgres - PDO 异常获取

mysql - 设计 mysql 数据库技巧和技术,以及一些 mysql 问题?

mysql - 条件分组 sql 查询

c# - SQL Server 连接计数问题

sql - 根据另一个表的日期范围更新一个表

mysql - 为 SELECT 查询中的变量赋值

sql - INSERT [...] ON CONFLICT 可以用于外键违规吗?

reactjs - 等待所有的 promise 成功

sql - 数字 ID 与字符串 ID

java - 如何在每次启动应用程序时创建一个新的数据库