SQL使用触发器进行约束

时间:2012-03-12 07:37:48

标签: sql triggers constraints

我正在研究触发器和约束。

我有一个问题要使用触发器(说实话,我不确定如何使用触发器..)

假设我们有一个教师表。

此教师表包含teacher_id,ssn,first_name,last_name,class_time

例如,

|teacher_id|ssn    | first_name | last_name | student_number| max_student
|1         |1234   | bob        | Smith     | 25            |25
|2         |1235   | kim        | Johnson   | 24            |21
|3         |1236   | kally      | Jones     | 23            |22

假设学生人数的最大数量为25个。(学生的最大数量将由教师定义,因此可以是任何数字,如10,22,25 ......)

并且学生想要添加bob的课程。但是,我想制作拒绝添加学生的触发器。(因为鲍勃的课程已经满了......)

但是,我不确定创建触发器的方式.. :( ..(这是第一次研究触发器......)

任何人都可以帮助创建示例代码来理解触发器部分吗?

1 个答案:

答案 0 :(得分:10)

首先,我认为这是一个数据规则,因此应该集中实施。也就是说,DBMS应该强制执行数据库约束(或等效),以防止所有应用程序写入错误数据(而不是依赖于每个应用程序的各个编码器来避免编写错误数据)。

其次,我认为AFTER触发器是合适的(而不是INSTEAD OF触发器。)

第三,可以使用外键和行级CHECK约束来强制执行此操作。

对于约束类型触发器,通常的想法是编写一个查询以返回错误数据,然后在触发器测试中将此结果为空。

您还没有发布表格的很多细节,所以我猜。我认为student_number是学生的理由;因为它听起来像一个标识符,所以我将更改名称,并假设学生的标识符是student_id

WITH EnrolmentTallies
     AS
     (
      SELECT teacher_id, COUNT(*) AS students_tally
        FROM Enrolment
       GROUP 
          BY teacher_id      
     ) 
SELECT * 
  FROM Teachers AS T
       INNER JOIN EnrolmentTallies AS E
         ON T.teacher_id = E.teacher_id
            AND E.students_tally > T.students_tally;

在SQL Server中,触发器定义如下所示:

CREATE TRIGGER student_tally_too_high ON Enrolment
AFTER INSERT, UPDATE
AS
IF EXISTS (
           SELECT * 
             FROM Teachers AS T
                  INNER JOIN (
                              SELECT teacher_id, COUNT(*) AS students_tally
                                FROM Enrolment
                               GROUP 
                                  BY teacher_id      
                             ) AS E
                                  ON T.teacher_id = E.teacher_id
                                     AND E.students_tally > T.students_tally
          )
BEGIN
RAISERROR ('A teachers''s student tally is too high to accept new students.', 16, 1);
ROLLBACK TRANSACTION;
RETURN 
END;

然而,还有一些进一步的考虑因素。在每个UPDATE之后对表执行这样的查询可能效率非常低。您应该使用UPDATE()(或COLUMNS_UPDATED,如果您认为可以依赖列排序)和/或deletedinserted概念表来限制查询的范围和什么时候开火您还需要确保正确地序列化事务以防止并发问题。虽然参与其中,但并不是非常复杂。

我强烈推荐本书Applied Mathematics for Database Professionals  By Lex de Haan, Toon Koppelaars,第11章(代码示例是Oracle,但可以轻松移植到SQL Server)。


有可能在没有触发器的情况下实现相同的目标。我们的想法是在注册中引用(teacher_id, students_tally)上的超级密钥,为此将保留一系列独特的学生出现次数,并测试该序列永远不会超过最大数量。

这是一些简单的SQL DDL:

CREATE TABLE Students 
(
 student_id INTEGER NOT NULL,
 UNIQUE (student_id)
);

CREATE TABLE Teachers 
(
 teacher_id INTEGER NOT NULL,
 students_tally INTEGER NOT NULL CHECK (students_tally > 0), 
 UNIQUE (teacher_id), 
 UNIQUE (teacher_id, students_tally)
);

CREATE TABLE Enrolment
(
 teacher_id INTEGER NOT NULL UNIQUE,
 students_tally INTEGER NOT NULL CHECK (students_tally > 0), 
 FOREIGN KEY (teacher_id, students_tally)
    REFERENCES Teachers (teacher_id, students_tally)
    ON DELETE CASCADE
    ON UPDATE CASCADE, 
 student_id INTEGER NOT NULL UNIQUE 
    REFERENCES Students (student_id),
 student_teacher_sequence INTEGER NOT NULL
    CHECK (student_teacher_sequence BETWEEN 1 AND students_tally)
 UNIQUE (teacher_id, student_id), 
 UNIQUE (teacher_id, student_id, student_teacher_sequence)
);

然后添加一些'help'存储过程/函数以维持更新序列。