首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >SQL使用触发器进行约束

SQL使用触发器进行约束
EN

Stack Overflow用户
提问于 2012-03-12 15:37:48
回答 1查看 19K关注 0票数 7

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

我有一个关于使用触发器的问题(老实说,我不太确定如何使用触发器。)

假设我们有一张教师桌。

该教师表包含teacher_id、ssn、first_name、last_name、class_time

例如,

代码语言:javascript
复制
|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的课已经满了。)

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

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

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2012-03-12 16:39:20

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

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

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

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

您还没有发布您的表的许多详细信息,所以我将猜测。我假设student_number是用来统计学生人数的;因为它听起来像是一个标识符,所以我将更改名称,并假定学生的标识符是student_id

代码语言:javascript
复制
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中,触发器定义如下所示:

代码语言:javascript
复制
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:

代码语言:javascript
复制
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”存储的procs/函数,以维护更新时的顺序。

票数 12
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/9663263

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档