SQL使用触发器进行约束

Dc *_*ing 5 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
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

one*_*hen 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;
Run Code Online (Sandbox Code Playgroud)

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

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

我强烈推荐" 数学专业人员应用数学 "一书作者: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)
);
Run Code Online (Sandbox Code Playgroud)

然后添加一些'help'存储过程/函数来维护更新时的序列.