范围触发器 - 员工工资

Posted

技术标签:

【中文标题】范围触发器 - 员工工资【英文标题】:Range Triggers - Salary of Employess 【发布时间】:2020-06-03 21:12:21 【问题描述】:

我有一张我称为FUNC 的表,我的所有员工都在其中,我还有另一个表可以注册不同的工作角色...每个员工都必须有一个工作角色。

我的工作角色表有它的ID、角色名称、角色的最低工资和角色的最高工资。

如果加薪在“最低工资和最高工资”范围内,我想创建一个触发器,每次我给某个员工RAISE检查 .

为了更容易理解,我将展示我到目前为止所做的事情......

CREATE TABLE FUNC(
    IDFUNC INT IDENTITY,
    NAME VARCHAR(30) NOT NULL,
    SALARY MONEY NOT NULL,
    ID_JOB INT,
    IDGESTOR INT
    )
GO

CREATE TABLE JOB(
    IDJOB INT IDENTITY,
    NAMEJOB VARCHAR(10) UNIQUE,
    MINIMUM MONEY NOT NULL,
    MAXIMUM MONEY NOT NULL,
)

/* CONSTRAINTS*/

ALTER TABLE FUNC ADD CONSTRAINT PK_FUNC
PRIMARY KEY(IDFUNC)
GO

ALTER TABLE JOB ADD CONSTRAINT PK_JOB
PRIMARY KEY(IDJOB)
GO

ALTER TABLE FUNC ADD CONSTRAINT FK_GESTOR
FOREIGN KEY(IDGESTOR) REFERENCES FUNC(IDFUNC)
GO

ALTER TABLE FUNC ADD CONSTRAINT FK_FUNC_JOB
FOREIGN KEY(ID_JOB) REFERENCES JOB(IDJOB)
GO

用这些值输入表格:

INSERT INTO JOB VALUES('MANAGER',5000,10000)
INSERT INTO JOB VALUES('SUPERVISOR',4000,7000)
INSERT INTO JOB VALUES('LEADER',2000,5000)
INSERT INTO JOB VALUES('ANALYST',1200,4000)
GO

IDJOB       NAMEJOB    MINIMUM               MAXIMUM
----------- ---------- --------------------- ---------------------
1           MANAGER    5000,00               10000,00
2           SUPERVISOR 4000,00               7000,00
3           LEADER     2000,00               5000,00
4           ANALYST    1200,00               4000,00

//

INSERT INTO FUNC VALUES('Name1',7000,1,NULL)
INSERT INTO FUNC VALUES('Name2',5000,2,1)
INSERT INTO FUNC VALUES('Name3',5000,2,1)
INSERT INTO FUNC VALUES('Name4',3000,3,2)
INSERT INTO FUNC VALUES('Name5',3400,3,2)
INSERT INTO FUNC VALUES('Name6',2800,3,3)
INSERT INTO FUNC VALUES('Name7',3200,3,3)
INSERT INTO FUNC VALUES('Name8',2000,4,4)
INSERT INTO FUNC VALUES('Name9',1800,4,4)
INSERT INTO FUNC VALUES('Name10',1500,4,5)
INSERT INTO FUNC VALUES('Name11',1300,4,5)
INSERT INTO FUNC VALUES('Name12',3000,4,6)
INSERT INTO FUNC VALUES('Name13',2000,4,6)
INSERT INTO FUNC VALUES('Name14',1900,4,7)
INSERT INTO FUNC VALUES('Name15',2100,4,7)
GO

IDFUNC         NAME                           SALARY               ID_JOB      IDGESTOR
------------- ------------------------------ --------------------- ----------- -----------
1             Name1                          7000,00               1           NULL
2             Name2                          5000,00               2           1
3             Name3                          5000,00               2           1
4             Name4                          3000,00               3           2
5             Name5                          3400,00               3           2
6             Name6                          2800,00               3           3
7             Name7                          3200,00               3           3
8             Name8                          2000,00               4           4
9             Name9                          1800,00               4           4
10            Name10                         1500,00               4           5
11            Name11                         1300,00               4           5
12            Name12                         3000,00               4           6
13            Name13                         2000,00               4           6
14            Name14                         1900,00               4           7
15            Name15                         2100,00               4           7

终于创建了这个触发器:

CREATE TRIGGER TRG_Salary
ON FUNC
FOR INSERT, UPDATE
AS
    DECLARE
    @MINIMUM MONEY, @MAXIMUM MONEY, @SALARY MONEY, @IDCARGO INT

    SELECT @IDJOB = IDJOB FROM JOB
    INNER JOIN INSERTED I
    ON IDJOB = I.ID_JOB
    WHERE IDFUNC = I.IDFUNC

    SELECT @MINIMUM = MINIMUM, @MAXIMUM = MAXIMUM
    FROM JOB WHERE IDJOB = @IDJOB

    SELECT @SALARY = I.SALARY FROM INSERTED I

    IF(@SALARY <@MINIMUM)
    BEGIN

        RAISERROR ('Salary Must be Higher than Minimum',16,1)
        ROLLBACK TRANSACTION
    END

    IF(@SALARY > @MAXIMUM)
    BEGIN

        RAISERROR ('SALARIO Must Be Lower than Maximum',16,1)
        ROLLBACK TRANSACTION
    END
GO

当我只更新一行时它工作正常......

前:

UPDATE FUNC SET SALARY = 15000 WHERE IDFUNC = 1
GO

它会显示一个错误,因为我的 IDFUNC=1 的 Employee 应该收到 5000 到 10000 之间,因为他是经理

我的问题是当我尝试一次更新所有行时...

如果我想为每个人加薪

UPDATE FUNC SET SALARY = SALARY*1.1

更新函数不检查我的触发器内的条件并更新薪水,无论它是在我的范围内还是在我的范围之外......

【问题讨论】:

INSERTED 有多行 - 您的代码仅选择 1 个随机 @IDJOB - 您需要使用基于集合的方法处理 inserted 您是否有任何链接解释如何使用“基于方法”以便我理解? 一切 SQL 都应该使用“基于集合的方法”——有很多链接,例如this 从设计/操作的角度来看,“部分”成功的触发器是噩梦。触发器旨在强制执行数据约束,而不是执行用户输入验证。我希望细微的差别很清楚。 如果您需要在 SQL 中执行此验证,那么我建议您编写一个 SP 或一个函数,可以为每个员工检查并显示相关错误之前您执行更新。 【参考方案1】:

问题是inserted 包含多行。与大多数其他数据库不同,SQL Server 不支持 per row 触发器(这是使用 for each row 子句实现的)。

您需要一次检查所有插入的行,如果检查失败,则全部拒绝。这是一种使用条件聚合的方法:

create trigger trg_salary
on func
for insert, update
as
    declare @isfailed int

    select @isfailed = max(
        case when i.salary not between j.minimum and j.maximum 
            then 1 
            else 0 
        end
    )
    from inserted i
    inner join job j on j.idjob = i.idjob

    if(@isfailed = 1)
    begin
        raiserror ('salary must be belong to the minimum-maximum range',16,1)
        rollback transaction
    end

go

【讨论】:

表JOB没有列IDFUNC "inner join job j on j.idfunc = i.idfunc" @marceloasr 你的问题是`WHERE IDFUNC = I.IDFUNC` - 即使你的表没有定义它。人们倾向于认为您发布的代码是正确的! 但是在这种情况下你不会写WHERE IDFUNC = I.IDFUNC,因为如果它们都在同一个表中,那和写1 = 1是一样的?无论如何检查我的答案。 @marceloasr:好的,我猜你想加入idjob。我相应地改变了我的答案。 @marceloasr:这是条件聚合:对每一行执行操作,然后应用max()。因此,如果任何记录的薪水超出范围,您会得到1【参考方案2】:

INSERTED 伪表包含 0-N 行,其中 N 是插入/更新的行数(可以为零!)。正因为如此,并且因为 SQL 针对“基于集合的操作”进行了优化,所以您需要构建一个查询来测试您的错误情况。

然而正因为如此,除非你想返回一个错误列表,否则即使存在多个错误,你也只能返回一个错误。

最好的做法是使用THROW 而不是RAISERROR

CREATE TRIGGER TRG_Salary ON FUNC
FOR INSERT, UPDATE
AS
BEGIN
    SET NOCOUNT ON;

    DECLARE @ERROR NVARCHAR(2048);

    SELECT TOP 1 @ERROR = CASE WHEN I.SALARY > MAXIMUM THEN 'SALARIO Must Be Lower than Maximum.' WHEN I.SALARY < MAXIMUM THEN 'SALARIO Must Be Higher than Minimum.' END
    FROM INSERTED I
    INNER JOIN JOB J on J.IDJOB = I.ID_JOB
    WHERE I.SALARY > MAXIMUM OR I.SALARY < MAXIMUM;

    IF @ERROR IS NOT NULL BEGIN
        ROLLBACK;
        THROW 51000, @ERROR, 1;  
    END;
END
GO

【讨论】:

它有效...但它不会更新范围内员工的薪水..,我想更新能够更新的薪水并返回错误其他...有可能吗? @marceloasr 这不是你的问题......事实上,这是一个完全不同的问题,答案也非常不同。 @marceloasr 您的 UI 将如何应对部分更新?这不会让您的用户感到困惑吗? @marceloasr 我建议研究INSTEAD OF 触发器。 触发器中的用户输入验证是一个非常糟糕的想法。触发器旨在强制执行数据约束。改为编写函数或 SP。【参考方案3】:

SQL Fiddle

MS SQL Server 2017 架构设置

CREATE TABLE FUNC(
    IDFUNC INT IDENTITY,
    NAME VARCHAR(30) NOT NULL,
    SALARY MONEY NOT NULL,
    ID_JOB INT,
    IDGESTOR INT
    )
GO

CREATE TABLE JOB(
    IDJOB INT IDENTITY,
    NAMEJOB VARCHAR(10) UNIQUE,
    MINIMUM MONEY NOT NULL,
    MAXIMUM MONEY NOT NULL,
)

/* CONSTRAINTS*/

ALTER TABLE FUNC ADD CONSTRAINT PK_FUNC
PRIMARY KEY(IDFUNC)
GO

ALTER TABLE JOB ADD CONSTRAINT PK_JOB
PRIMARY KEY(IDJOB)
GO

ALTER TABLE FUNC ADD CONSTRAINT FK_GESTOR
FOREIGN KEY(IDGESTOR) REFERENCES FUNC(IDFUNC)
GO

ALTER TABLE FUNC ADD CONSTRAINT FK_FUNC_JOB
FOREIGN KEY(ID_JOB) REFERENCES JOB(IDJOB)
GO

INSERT INTO JOB VALUES('MANAGER',5000,10000)
INSERT INTO JOB VALUES('SUPERVISOR',4000,7000)
INSERT INTO JOB VALUES('LEADER',2000,5000)
INSERT INTO JOB VALUES('ANALYST',1200,4000)
GO
INSERT INTO FUNC VALUES('Name1',7000,1,NULL)
INSERT INTO FUNC VALUES('Name2',5000,2,1)
INSERT INTO FUNC VALUES('Name3',5000,2,1)
INSERT INTO FUNC VALUES('Name4',3000,3,2)
INSERT INTO FUNC VALUES('Name5',3400,3,2)
INSERT INTO FUNC VALUES('Name6',2800,3,3)
INSERT INTO FUNC VALUES('Name7',3200,3,3)
INSERT INTO FUNC VALUES('Name8',2000,4,4)
INSERT INTO FUNC VALUES('Name9',1800,4,4)
INSERT INTO FUNC VALUES('Name10',1500,4,5)
INSERT INTO FUNC VALUES('Name11',1300,4,5)
INSERT INTO FUNC VALUES('Name12',3000,4,6)
INSERT INTO FUNC VALUES('Name13',2000,4,6)
INSERT INTO FUNC VALUES('Name14',1900,4,7)
INSERT INTO FUNC VALUES('Name15',2100,4,7)
GO

CREATE TRIGGER TRG_Salary
ON FUNC
FOR INSERT, UPDATE
AS

    IF EXISTS
    (
        SELECT 1
        FROM JOB J
        INNER JOIN INSERTED F
        ON J.IDJOB = F.ID_JOB
        WHERE F.Salary < J.Minimum OR F.Salary > J.Minimum
    )    
    BEGIN
        RAISERROR ('SALARIO Must Be Lower than Maximum and Greater than Minimum',16,1)
        ROLLBACK TRANSACTION
    END
GO

查询 1

--UPDATE FUNC SET SALARY = 15000 WHERE IDFUNC = 1

UPDATE FUNC SET SALARY = SALARY*1.1

Results: SALARIO 必须小于最大值且大于最小值

【讨论】:

以上是关于范围触发器 - 员工工资的主要内容,如果未能解决你的问题,请参考以下文章

如何编写触发器来检查工资是不是超出工资的最小或最大范围pl-sql Oracle

在插入新记录之前触发检查部门的平均工资

更新触发器..没有按预期工作?

SQL Server 触发器:更新查询新旧数据

MySQL 使用游标触发并循环更新错误值

oracle学习9.触发器以及应用场景