触发插入校验和

trigger for insert to check sum

我正在尝试创建一个触发器来检查 table 中特定 'work area' 的三种不同木材类型的插入总和是否应为 10(10 代表 100%)称为 SpaceMixes。 'Percentage' 是存储特定作品 space 中木材类型百分比的列(木材类型被命名为 'H'、'P' 或 'F')。

这些作品 space 由组合的主键 (AreaNr, SpaceNr) 定义,即使 SpaceMixes 的整个 PK 是 (AreaNr, SpaceNr, WoodType)。下面的触发器检查一个工作中总和只能是 10 space,但问题是第一个值也必须是 10 与此代码。但是我希望能够为作品 space 插入像“2+5+3”这样的东西,它不应该允许像 2+5+5 这样的东西,因为那样会超过 10 的总和工作 space(查看预期结果)。

我目前使用的触发器:

ALTER TRIGGER trg_Sum ON SpaceMixes
FOR INSERT AS

DECLARE @sum INT

SELECT @sum = sum(SpaceMixes.Percentage)
FROM inserted, SpaceMixes
WHERE inserted.AreaNr = SpaceMixes.AreaNr AND inserted.SpaceNr = SpaceMixes.SpaceNr
GROUP BY inserted.AreaNr

IF NOT (@sum = 10)

BEGIN
RAISERROR ('The sum of the percentages must be 10 for each work space!',16, 1)
ROLLBACK TRANSACTION
END

期望的结果: 下面的前两个插入应该工作得很好,但我希望在第三个插入时激活触发器,因为百分比“5”将超过工作的 10 (5+3+5=13) 的总和 space (2, 3)。换句话说,只有在给定百分比为 2 而不是 5 时才允许第三次插入。

INSERT INTO SpaceMixes VALUES (2, 3, 'P', 5)
INSERT INTO SpaceMixes VALUES (2, 3, 'F', 3)
INSERT INTO SpaceMixes VALUES (2, 3, 'H', 5)

有人知道如何修复此触发器以使此求和有效吗?

首先,您应该在SpaceMixes table中包含一个CHECK contain,以确保没有一条记录超过最大百分比值:

ALTER TABLE SpaceMixes ADD CONSTRAINT CHK_Percentage CHECK  ( [Percentage] <=10 )

这个触发器:

CREATE TRIGGER [dbo].[trg_Sums] ON [dbo].[SpaceMixes]
   AFTER UPDATE, INSERT
AS  

-- First check Work spaces having only two records (not yet complete).
-- These should have a percentage not greater than 10, allowing for the insertion of
-- the last work space member.
IF EXISTS (
    SELECT TOP 1 NULL
    FROM (
       SELECT SUM(Percentage) AS TotalPercentage
       FROM SpaceMixes
       WHERE AreaNr = (SELECT AreaNr FROM inserted) AND 
             SpaceNr = (SELECT SpaceNr FROM inserted) 
       GROUP BY AreaNr, SpaceNr
       HAVING COUNT(*) = 2) t
   WHERE t.TotalPercentage > 10 
)
BEGIN
    RAISERROR ('The sum of the percentages cannot be greater than 10 for any sub-workspace!',16, 1)
    ROLLBACK TRANSACTION
END

-- Now check Work spaces having exactly three records (i.e. complete workspace).  
-- These should have a total percentage equal to 10.
IF EXISTS (
   SELECT TOP 1 NULL
   FROM (SELECT SUM(Percentage) AS TotalPercentage
         FROM SpaceMixes
         WHERE AreaNr = (SELECT AreaNr FROM inserted) AND 
               SpaceNr = (SELECT SpaceNr FROM inserted) 
         GROUP BY AreaNr, SpaceNr
         HAVING COUNT(*) = 3) t
   WHERE t.TotalPercentage <> 10    
)
BEGIN
    RAISERROR ('The sum of the percentages must be 10 for each work space!',16, 1)
    ROLLBACK TRANSACTION
END

应该完成这项工作。请注意,除了 INSERT 之外,您还应该为 UPDATE.

定义触发器

已插入这两条记录:

INSERT INTO SpaceMixes VALUES (2, 3, 'P', 5)
INSERT INTO SpaceMixes VALUES (2, 3, 'F', 3)

以下两个 INSERTUPDATE 查询都会引发错误:

INSERT INTO SpaceMixes VALUES (2, 3, 'H', 5) -- SUM = 13
INSERT INTO SpaceMixes VALUES (2, 3, 'H', 1) -- SUM = 9

-- UPDATE SpaceMixes after inserting a percentage of 2 for WOOD TYPE = 'H'
UPDATE SpaceMixes
SET Percentage = 6
WHERE AreaNr = 2 AND SpaceNr =3 And WoodType = 'F'

一般来说,尽可能避免将数据完整性要求分散到同一 table 的不同行是一个很好的数据建模规则。如您所见,它会在执行约束时产生问题。

您可能需要考虑以下折衷方案。您可以将所有三个值放在同一行中:

create table SpaceMixes(
  AreaNr    int not null,
  SpaceNr   int not null,
  P_Wood    int,
  F_Wood    int,
  H_Wood    int,
  ...
  constraint PK_SpaceMixes primary key( AreaNr, SpaceNr ),
  constraint CK_Exactly10Required check( IsNull( P_Wood + F_Wood + H_Wood, 10 ) = 10 ),
  constraint CK_Total10Exceeded check( IsNull( P_Wood, 0 ) + IsNull( F_Wood, 0 ) + IsNull( H_Wood, 0 ) <= 10 ),
);

优点是大部分你想要的都可以通过正常的检查约束来实现。 CK_Exactly10Required 约束将强制执行以下规则:如果所有三个字段都有一个值,则总数必须等于 10。CK_Total10Exceeded 约束将强制执行以下规则:无论定义多少个值,总数都不能超过10. 您可以根据需要添加其他约束(例如,任何值都不能为负)以改进检查。不需要触发器 - 无论如何,为此目的。

权衡是您必须添加新字段而不是新行才能处理第四种木材。这有多重要取决于这种情况发生的可能性。

编辑:即使您不想或不能反规范化,我想我也可以提交我自己的触发器供您考虑。您实际上不需要单独的检查约束来验证每个值是否为 10 或更小,因为触发器也会捕获该条件。我建议使用检查约束来捕获负值。这也可以放在触发器中,但应在尽可能低的级别进行检查。

触发。这将适用于插入 更新。不需要删除触发器,因为没有以无效金额结束的删除操作。正如您在查询中看到的那样,它只是验证三个条目的总数必须恰好为 10,否则必须为 10 或更少。只需检查任何偏差并在发现时提出错误。

create trigger SpaceMixes_IU
on SpaceMixes
after insert, update as
declare
    @Result int;

with
KeyList as(
    select  distinct AreaNr, SpaceNr
    from    inserted
)
select  @Result = case when Count( * ) = 3
            then case Sum( sm.Percentage ) when 10 then 0 else 1 end
        else case when Sum( sm.Percentage ) <= 10 then 0 else 1 end
        end
from    SpaceMixes  sm
join    KeyList     kl
    on  kl.AreaNr   = sm.AreaNr
    and kl.SpaceNr  = sm.SpaceNr
group by sm.AreaNr, sm.SpaceNr;

if @Result > 0 begin
    Rollback;
    RaisError( 'Workspace percentages not valid!', 16, 0 );
end;