sql - 创建触发器 - SQL 服务器

标签 sql sql-server-2008 triggers

一个学生要去一所学校,他将在那里支付每月的费用。该值将为 (FatherSalary + MotherSalary)*0.05

我昨天才开始研究触发器,我做了一个但是我得到了错误

Msg 515, Level 16, State 2, Procedure TR_Calc_Value, Line 25
Cannot insert the value NULL into column 'IDStudent', table 'HW32.dbo.Enrollment'; column does not allow nulls. INSERT fails.

当我在表 Enrollment 中插入值时。任何帮助如何解决这个问题?

USE master;

IF DB_ID (N'HW32') IS NOT NULL
   DROP DATABASE HW32;

CREATE DATABASE HW32;

USE HW32
CREATE TABLE Family(
  IDFamily int IDENTITY(1,1),
  FirstName nchar(20) NOT NULL,
  LastName nchar(20) NOT NULL,
  Gender nchar(1)  NOT NULL,
  Salary money,
  CONSTRAINT PK_Family PRIMARY KEY(IDFamily),
  CONSTRAINT CK_Family_Gender CHECK (Gender IN ('M','F'))
) 

CREATE TABLE Student(
  IDStudent int IDENTITY(1,1),
  FirstName nchar(20) NOT NULL,
  LastName nchar(20) NOT NULL,
  CONSTRAINT PK_Student PRIMARY KEY(IDStudent)
)

CREATE TABLE Filiation(
  IDStudent int,
  IDFamily int,
  Filiation nchar(20) NOT NULL,
  CONSTRAINT FK_Filiation_IDStudent FOREIGN KEY (IDStudent)
  REFERENCES Student(IDStudent),
  CONSTRAINT FK_Filiation_IDFamily FOREIGN KEY (IDFamily)
  REFERENCES Family(IDFamily),
  CONSTRAINT PK_Filiation PRIMARY KEY(IDStudent,IDFamily)
)

CREATE TABLE Enrollment(
  IDEnrollment int IDENTITY(1,1),
  IDStudent int NOT NULL,
  Status nchar(20) NOT NULL,
  MonthlyPayment money,
  CONSTRAINT PK_Enrollment PRIMARY KEY(IDStudent), 
  CONSTRAINT FK_Enrollment_IDStudent FOREIGN KEY (IDStudent)
  REFERENCES Student(IDStudent),
  CONSTRAINT CK_Enrollment_Status CHECK(Status IN('Acepted','Rejected')),
  CONSTRAINT UC_Enrollment UNIQUE (IDEnrollment)
)

USE HW32
GO
CREATE TRIGGER TR_Calc_Value 
ON Enrollment 
AFTER INSERT, UPDATE AS
    DECLARE @monthlyPayment money, @sFather money, @sMother money
BEGIN
    SET @sFather = (SELECT FAM.Salary
            FROM Family FAM 
            LEFT JOIN Filiation F ON F.IDFamily = FAM.IDFamily
            LEFT JOIN inserted I ON I.IDStudent = F.IDStudent
            WHERE F.IDStudent = I.IDStudent AND FAM.Gender = 'M')

    SET @sMother = (SELECT FAM.Salary 
            FROM Family FAM 
            LEFT JOIN Filiation F ON F.IDFamily = FAM.IDFamily
            LEFT JOIN inserted I ON I.IDStudent = F.IDStudent
            WHERE F.IDStudent = I.IDStudent AND FAM.Gender = 'F')

    SET @monthlyPayment = ((@sFather + @sMother) * 0.05)

    INSERT INTO Enrollment (MonthlyPayment) VALUES (@monthlyPayment)
END
GO

USE HW32
INSERT INTO Family VALUES('John', 'Smith', 'M', 800)
INSERT INTO Family VALUES('Anna', 'Smith', 'F', 800)

INSERT INTO Student VALUES('Carl', 'Smith')

INSERT INTO Filiation VALUES(1, 1, 'Father')
INSERT INTO Filiation VALUES(1, 2, 'Mother')

INSERT INTO Enrollment (IDStudent, Status) VALUES(1, 'Accepted')

最佳答案

您不想在触发器中INSERT 行 - 这些行已经添加。相反,您想执行一个UPDATE:

CREATE TRIGGER TR_Calc_Value ON Enrollment AFTER INSERT, UPDATE AS
BEGIN
   UPDATE E 
   set MonthlyPayment = (

      select 0.05 * SUM(Salary) 
      from Family FAM 
      inner join Filiation F 
      on FAM.IDFamily= F.IDFamily 
      where F.IDStudent = I.IDStudent
      )

   from Enrollment E
   inner join inserted I
   on E.IDEnrollment=  I.IDEnrollment

注意事项

  • 上面的代码正确处理了包含多行的inserted
  • 我不区分父亲和母亲(因为他们似乎受到同等对待)
  • 如果有多个父亲和母亲与学生相关联,他们都会被包括在内。这是否正确是值得商榷的,并且可能无法使用更好的约束 - 但原始触发器也根本无法很好地应对这种情况。

根据我的评论,我还建议不要存储此信息,如果它总是可以从其他表中重新计算的话(不确定在这种情况下是否正确,但如果它应该反射(reflect)其他表,您将需要更多触发器来保持一切一致。

作为 per-marc 的已删除答案,养成在执行插入时始终指定列列表也是一个好习惯。这不是此特定错误的原因,但有助于更好地记录您的意图,并有助于通过目视检查快速消除可能的错误。

关于sql - 创建触发器 - SQL 服务器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13669220/

相关文章:

MySQL 触发器和过程

mysql - 不能对多列使用全文搜索

mysql - 日期 SQL 介于

sql-server - SQL 服务器 : loading database file

sql-server - 在 SQL UDF 中插入记录并返回 Id?

visual-studio - 如何在 Visual Studio 2008 中设置 SSRS ConsumeContainerWhitespace 属性?

mysql - 'DELIMITER' 附近的 SQL 语法错误

sql - Rails 5 - ActiveRecord - 过滤 has_many 关系包含但不排除空值

c# - sql 到 linq 查询转换

php - MYSQL中如何使用触发器在指定日期到达时对表进行更改?