触发插入校验和
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)
以下两个 INSERT
和 UPDATE
查询都会引发错误:
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;
我正在尝试创建一个触发器来检查 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)
以下两个 INSERT
和 UPDATE
查询都会引发错误:
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;