【问题标题】:Trigger/Constraint to restrict inserts or updates触发器/约束以限制插入或更新
【发布时间】:2014-02-06 14:24:56
【问题描述】:

我有一张桌子,看起来像

MyTable (
Student ID, 
Exam)

我想放置一个触发器/约束,描述:

学生 ID 可以出现多次,并且不必是唯一的。但是,这对 (Student ID, "French") 只能出现一次。所以每个学生只能有一个“法语”条目。法语是硬编码的

身份证考试 0001 德语 0001 历史 0001 法语 0001 法语

尝试将“德语”字段更新为法语也会失败

到目前为止我已经尝试过

CREATE OR REPLACE TRIGGER MyTrigger BEFORE INSERT OR UPDATE ON MyTable  
FOR EACH ROW
DECLARE
    rowsCount INTEGER;
BEGIN 
    select  count(*)
    INTO rowsCount
    from    MyTable  sc
    where SC.SC_TYPE = 'FRENCH' and :new.StudentID = sc.StudentID;
IF  rowsCount > 1    
THEN
     raise_application_error('-20098','You cannot have more than one French record per student.');
END IF;
end;

虽然这会引发 Mutate 错误。无论如何,这与完整性有关,所以我确信约束会更好,但希望得到一些关于如何实现这一点的建议。

【问题讨论】:

  • 所以对French的限制是具体的,应该允许他们有German的多行?

标签: sql oracle plsql triggers


【解决方案1】:

是的,每个学生法语只能出现一次,但其他所有内容都可以出现多次。

我在想也许这最好用索引来解决?类似于:Oracle SQL Constraint where clause

CREATE UNIQUE INDEX MyIndex ON MyTable(
  CASE WHEN Exam= 'French'
       THEN StudentID
       ELSE NULL
  END
);

【讨论】:

  • 那也行不通。如果您有任何学生多次参加相同的考试,则第二次插入将失败。即使您将索引更改为(StudentID, CASE WHEN EXAM='French' THEN StudentID ELSE NULL END),它仍然会在第二次为给定学生插入非“法语”语言时失败,至少在 Oracle 11.1 下是这样。 SQLFiddle here
  • 在索引定义中只有 case 语句应该没问题。如果您从 sqlfiddle 的索引中删除 student_id 列,那么一切都很好。
【解决方案2】:

由于限制的特殊性(仅限制法语),我认为这不能通过限制来完成(尽管我很乐意学习其他方式)。解决它的一种方法是使用语句触发器:

CREATE OR REPLACE TRIGGER NO_DUPLICATE_FRENCH_TRIGGER
  BEFORE INSERT OR UPDATE 
  ON MY_TABLE
  -- NOTE: NO 'FOR EACH ROW', which means this is fired once for each 
  -- statement executed, rather than once for each row modified.
DECLARE
  nMax_count NUMBER;
BEGIN
  SELECT MAX(COUNT_VAL)
    INTO nMax_count
    FROM (SELECT STUDENTID, COUNT(*) AS COUNT_VAL
            FROM MY_TABLE
            WHERE EXAM = 'FRENCH'
            GROUP BY STUDENTID);

  IF nMax_count > 1 THEN
    RAISE SOME_EXCEPTION;
  END IF;
END NO_DUPLICATE_FRENCH_TRIGGER;

语句触发器的优点是它们不会像行触发器那样受到“变异表”问题的影响。不过,这有点麻烦,引入了全表扫描,如果表很大可能是性能问题,但至少是一个解决方案。

分享和享受。

【讨论】:

    【解决方案3】:

    我建议您创建 3 个触发器和一个包来处理此验证。

    • 1 个包,其中包含将由触发器调用的 PL/SQL 表和过程。
    • 1 before statement trigger 以清除 PL/SQL 表。
    • 在每行触发器之前(或之后)1 次,以将您正在插入/更新的记录添加到 PL/SQL 表中。
    • 根据您已填充的 PL/SQL 表执行验证的 1 个语句后触发器。

    您可以只将 Exam = 'French' 的记录添加到 PL/SQL 表中,并检查该学生是否已经在数据库中进行了法语考试。

    【讨论】:

      【解决方案4】:

      这个怎么样?

      alter table MyTable add constraint std_exam unique (Student ID, Exam);

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-06-15
        • 2013-01-24
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-02-26
        • 1970-01-01
        相关资源
        最近更新 更多