我正在尝试为数据库设置触发器(我使用的是Microsoft SQL Server)。
我有两张桌子
create table atbv_Sales_Products
(
ProductID integer,
TotalQuantity integer
);
insert into atbv_Sales_Products values (1, 1);
insert into atbv_Sales_Products values (2, 2);
insert into atbv_Sales_Products values (3, 20);
insert into atbv_Sales_Products values (4, 10);
insert into atbv_Sales_Products values (5, 20);
insert into atbv_Sales_Products values (6, 10);
insert into atbv_Sales_Products values (7, 5);
insert into atbv_Sales_Products values (8, 50);
insert into atbv_Sales_Products values (9, 1);
create table atbv_Sales_OrdersLines
(
OrderID integer,
ProductID integer,
Amount integer
);
insert into atbv_Sales_OrdersLines values (6, 4, 1);
insert into atbv_Sales_OrdersLines values (6, 6, 1);
insert into atbv_Sales_OrdersLines values (6, 1, 1);
insert into atbv_Sales_OrdersLines values (47, 4, 1);
insert into atbv_Sales_OrdersLines values (6, 9, 1);
insert into atbv_Sales_OrdersLines values (5, 7, 1);
insert into atbv_Sales_OrdersLines values (6, 2, 2);
还有一个插入表(它实际上是自动生成的,但为了清楚起见,我们把它放在这里
create table Inserted
(
OrderID integer,
ProductID integer,
Amount integer
);
insert into Inserted values (48, 4, 9);
insert into Inserted values (48, 1, 10);
insert into Inserted values (48, 8, 100);
insert into Inserted values (48, 2, 1);
为了便于理解,这些表格如何以图形方式显示:
产品表
OrdersLines表
插入表格
现在触发器应检查是否在超过TotalQuantity之前插入Amount值+ Amount值(这是一个静态值。或者换句话说,当新订单进入时它不会改变)如果是这样的话回滚变化
对于我使用代码的这部分进行过滤
IF EXISTS (select p.ProductID
from atbv_Sales_Products p
join Inserted i
on p.ProductID = i.ProductID
join atbv_Sales_OrdersLines ol
on p.ProductID = i.ProductID
group by i.ProductID, i.Amount, p.TotalQuantity
having (SUM(ol.Amount) + i.Amount) > p.TotalQuantity)
然后我一直在尝试使用代码的以下代码部分来回滚更改并提醒错误
BEGIN
DECLARE @ProductID NVARCHAR(60)
SET @ProductID = (SELECT p.ProductID
FROM atbv_Sales_Products p
JOIN inserted i
ON i.ProductID = p.ProductID)
RAISERROR ('----There is not enough items number (%s) left----', 18, 1, @ProductName) ROLLBACK TRANSACTION
RETURN
END
如果只有一个插入行,哪个工作正常,但如果当前示例中有多行,我不知道该怎么做。我想我已经在某个地方阅读了我可以创建一个临时表但又不知道如何将那些被代码的第一部分过滤掉的值插入其中,然后使用这些值显示在错误信息。
答案 0 :(得分:1)
如果您愿意,可以使用临时表,但我认为这会引入不必要的步骤。我要做的只是将所有不良产品ID序列化为变量,然后检查该变量是否为空(这将替换您的EXISTS
语句。
declare @BadProducts varchar(max)
select @BadProducts =
stuff((select ',' + p.ProductId
from atbv_Sales_Products p
join Inserted i
on p.ProductID = i.ProductID
join atbv_Sales_OrdersLines ol
on p.ProductID = i.ProductID
group by i.ProductID, i.Amount, p.TotalQuantity
having (SUM(ol.Amount) + i.Amount) > p.TotalQuantity
for xml path('')), 1, 1, '')
if @BadProducts is not null
begin
raiserror('These products are bad: %s', 16, 1, @BadProducts)
return
end
替代方法是将行插入临时表或表变量(如您所建议的那样),然后对临时表执行存在检查或@@rowcount
检查,如果匹配,则序列化产品ID与第一个示例大致相同(除了用你的临时表替换整个大子查询)。像这样:
declare @badProductsTable table
(
ProductId int
)
insert into @badProductsTable (ProductId)
select p.ProductId
from atbv_Sales_Products p
join Inserted i
on p.ProductID = i.ProductID
join atbv_Sales_OrdersLines ol
on p.ProductID = i.ProductID
group by i.ProductID, i.Amount, p.TotalQuantity
having (SUM(ol.Amount) + i.Amount) > p.TotalQuantity
if @@rowcount > 0
-- or you could do
-- if exists (select 1 from @badProductsTable)
begin
select @BadProducts =
stuff((select ',' + ProductId
from @badProductsTable
for xml path('')), 1, 1, '')
raiserror('These products are bad: %s', 16, 1, @BadProducts)
return
end
答案 1 :(得分:0)
你的If Exists陈述真的是你需要的。这个脚本对我有用......
-- DROP table atbv_Sales_Products
create table atbv_Sales_Products
(
ProductID integer,
TotalQuantity integer
);
insert into atbv_Sales_Products values (1, 10);
-- DROP table atbv_Sales_OrdersLines
create table atbv_Sales_OrdersLines
(
OrderID integer,
ProductID integer,
Amount integer
);
insert into atbv_Sales_OrdersLines values (100, 1, 4);
CREATE TRIGGER MyTrigger ON atbv_Sales_OrdersLines AFTER INSERT, UPDATE AS
IF EXISTS (
SELECT *
FROM (SELECT ProductID, SUM(Amount) as [Amount] FROM Inserted GROUP BY ProductID) I
LEFT JOIN (SELECT ProductID, SUM(Amount) as [Amount] FROM atbv_Sales_OrdersLines X WHERE NOT EXISTS (SELECT * FROM Inserted Y WHERE Y.OrderID = X.OrderID) GROUP BY ProductID) OL ON OL.ProductID = I.ProductID
JOIN atbv_Sales_Products P ON P.ProductID = I.ProductID
WHERE I.Amount + ISNULL(OL.Amount, 0) > P.TotalQuantity
)
THROW 51000, 'My Error Message.', 1;
GO
-------------
SELECT * FROM atbv_Sales_Products
SELECT * FROM atbv_Sales_OrdersLines
insert into atbv_Sales_OrdersLines values (101, 1, 4);
insert into atbv_Sales_OrdersLines values (102, 1, 2);
insert into atbv_Sales_OrdersLines values (103, 1, 1);
另外,考虑找一种方法来使用CHECK约束而不是触发器。触发器通常会导致维护问题和性能问题。
最后,这个例子只是学术性的。由于订单进入多天且可用数量波动,此解决方案将无法正常工作。它可以说明一点,但我希望这只是出于学术目的。