如果满足条件,防止插入

Hug*_*usa 7 sql postgresql triggers database-design constraints

我有这样一张桌子Content:

id | text | date | idUser ? User | contentType 
Run Code Online (Sandbox Code Playgroud)

另一张桌子Answer:

idAnswer ? Content | idQuestion ? Content | isAccepted
Run Code Online (Sandbox Code Playgroud)

我想确保Answer日期大于Question日期.一个问题是一个Content带有contentType="问题".

我尝试使用以下触发器解决此问题,但是当我尝试插入时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
Run Code Online (Sandbox Code Playgroud)

触发:

CREATE TRIGGER check_valid_answer 
AFTER INSERT ON "Answer"
FOR EACH ROW EXECUTE PROCEDURE check_valid_date_answer();
Run Code Online (Sandbox Code Playgroud)

触发功能:

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;$$;
Run Code Online (Sandbox Code Playgroud)

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

Erw*_*ter 8

你的基本方法是合理的.触发器是有效的解决方案.它应该工作除了3个问题:

1)您的命名约定:

我们需要确定您的确切表定义,但证据就在那里.错误消息说:has no field "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."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;
Run Code Online (Sandbox Code Playgroud)

妥善解决是使用合法的,小写的名字完全和完全避免这些问题.这包括你不幸的表名和列名date,它是标准SQL中的保留字,不应该用作标识符 - 即使Postgres允许它.

3)应该是一个BEFORE触发器

CREATE TRIGGER insbef_check
BEFORE INSERT ON "Answer"
FOR EACH ROW EXECUTE PROCEDURE trg_answer_insbef_check();
Run Code Online (Sandbox Code Playgroud)

您希望在执行任何其他操作之前中止无效插入.

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