SQL использует триггер для ограничения - PullRequest
5 голосов
/ 12 марта 2012

Я изучаю триггеры и ограничения.

И у меня возник вопрос по использованию триггера (Если честно, я не совсем уверен, как использовать триггер ..)

Допустим, мыиметь таблицу учителей.

И эта таблица учителей содержит имя учителя, 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 ...)

, и студент хочетдобавьте класс боба.Тем не менее, я хочу сделать триггер, который отклоняет добавление ученика. (Потому что класс Боба уже заполнен ..)

Однако я не совсем уверен, как создать триггер .. :( ..(это первый раз, чтобы изучить триггер ....)

Может ли кто-нибудь помочь создать пример кода для понимания триггерной части?

1 Ответ

10 голосов
/ 12 марта 2012

Во-первых, я думаю, что это правило данных и поэтому должно применяться централизованно.То есть должно быть ограничение базы данных (или его эквивалент), применяемое СУБД, которое не позволяет всем приложениям записывать неверные данные (вместо того, чтобы полагаться на то, что отдельные кодировщики каждого приложения воздерживаются от записи неверных данных).

Во-вторых, я думаю, что уместен триггер 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, если вы считаете, что можно упорядочить столбцы) и / или концептуальные таблицы deleted и inserted, чтобы ограничить объем запроса и его запуск.Вам также необходимо убедиться, что транзакции правильно сериализованы для предотвращения проблем параллелизма.Несмотря на это, это не очень сложно.

Я настоятельно рекомендую книгу Прикладная математика для специалистов по базам данных. Лекс де Хаан, Тун Коппелаарс , глава 11 (примеры кода - Oracle, но можетможет быть легко перенесен на SQL Server).


Возможно добиться того же без триггеров.Идея состоит в том, чтобы создать суперключ на (teacher_id, students_tally) для ссылки в Enrollment, для которого будет поддерживаться последовательность уникальных вхождений учащихся с проверкой, что последовательность никогда не превысит максимальное число.

Вот некоторыеBDN 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)
);

Затем добавьте некоторые сохраненные «справочные» процессы / функции, чтобы сохранить последовательность при обновлении.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...