此过程包含几个步骤,这些步骤反映在数据库的各个表中:
制作 - >使用
之类的内容UPDATE
到库存表
UPDATE STOR SET
STOR.BLOC1 = T.BLOC1,
STOR.BLOC2 = T.BLOC2,
STOR.BLOC3 = T.BLOC3,
STOR.PRODUCTION = T.PROD,
STOR.DELTA = T.DELTA
FROM BLDG B INNER JOIN STOR S
ON S.B_ID = B.B_ID
CROSS APPLY dbo.INVENTORIZE(B.B_ID) AS T;
以上使用TRIGGER
提供日志表,如下所示:
CREATE TRIGGER trgrCYCLE
ON STOR
FOR UPDATE
AS
INSERT INTO dbo.INVT
(TS, BLDG, PROD, ACT, VAL)
SELECT CURRENT_TIMESTAMP, B_ID, PRODUCTION,
CASE WHEN DELTA < 0 THEN 'SELL' ELSE 'BUY' END,
DELTA
FROM inserted WHERE COALESCE(DELTA,0) <> 0
最后,每次更新 INSERT
一行到我在上面添加到TRIGGER的财务表中:
INSERT INTO dbo.FINS
(COMPANY, TS, COST2, BAL)
SELECT CORP, CURRENT_TIMESTAMP, COST,
((SELECT TOP 1 BAL FROM FINS WHERE COMPANY = CORP ORDER BY TS DESC)- COST)
FROM inserted WHERE COALESCE(COST,0) <> 0
问题在于这一行:
((SELECT TOP 1 BAL FROM FINS WHERE COMPANY = CORP ORDER BY TS DESC)- COST)
用于计算帐户的最新余额。但是因为CROSS APPLY
将所有INSERTS
视为一个批处理,所以计算是在同一个最后一个记录中完成的,并且我得到了一个不正确的余额数字。例如:
COST BALANCE
----------------
1,000 <-- initial balance
-150 850
-220 780 <-- should be 630
解决这个问题的方法是什么? FINS
表上的触发器,而不是余额计算?
答案 0 :(得分:2)
了解查询中的现有逻辑
对于满足连接条件的集合或批处理, UPDATE
语句只会触发trigger
一次,Inserted语句将包含所有正在更新的记录。这是因为BATCH处理不是因为CROSS APPLY
而是因为UPDATE
。
在你的查询中
SELECT CORP, CURRENT_TIMESTAMP, COST,
((SELECT TOP 1 BAL FROM FINS WHERE COMPANY = CORP ORDER BY TS DESC)- COST)
FROM inserted WHERE COALESCE(COST,0) <> 0
对于来自外部查询的每个CORP,将返回相同的BAL。
(SELECT TOP 1 BAL FROM FINS WHERE COMPANY = CORP ORDER BY TS DESC)
话虽这么说,每当CORP ='XYZ'时你的内部查询将被1000(你的例子中使用的值)替换
SELECT CORP, CURRENT_TIMESTAMP, COST, (1000- COST)
FROM inserted WHERE COALESCE(COST,0) <> 0
现在您的inserted语句包含所有要插入的记录。因此,每个记录的成本将减去1000.因此,您将获得意想不到的结果。
建议的解决方案
根据我的理解,你想要计算一些累积频率的东西。或者上次运行总计
问题陈述的数据准备。使用我的虚假数据给你一个想法。
--Sort data based on timestamp in desc order
SELECT PK_LoginId AS Bal, FK_RoleId AS Cost, AddedDate AS TS
, ROW_NUMBER() OVER (ORDER BY AddedDate DESC) AS Rno
INTO ##tmp
FROM dbo.M_Login WHERE AddedDate IS NOT NULL
--Check how data looks
SELECT Bal, Cost, Rno, TS FROM ##tmp
--Considering ##tmp as your inserted table,
--I just added Row_Number to apply Top 1 Order by desc logic
+-----+------+-----+-------------------------+
| Bal | Cost | Rno | TS |
+-----+------+-----+-------------------------+
| 172 | 10 | 1 | 2012-12-05 08:16:28.767 |
| 171 | 10 | 2 | 2012-12-04 14:36:36.483 |
| 169 | 12 | 3 | 2012-12-04 14:34:36.173 |
| 168 | 12 | 4 | 2012-12-04 14:33:37.127 |
| 167 | 10 | 5 | 2012-12-04 14:31:21.593 |
| 166 | 15 | 6 | 2012-12-04 14:30:36.360 |
+-----+------+-----+-------------------------+
从上次运行余额中减去费用的替代逻辑。
--Start a recursive query to subtract balance based on cost
;WITH cte(Bal, Cost, Rno)
AS
(
SELECT t.Bal, 0, t.Rno FROM ##tmp t WHERE t.Rno = 1
UNION ALL
SELECT c.Bal - t.Cost, t.Cost, t.Rno FROM ##tmp t
INNER JOIN cte c ON t.RNo - 1 = c.Rno
)
SELECT * INTO ##Fin FROM cte;
SELECT * FROM ##Fin
<强>输出强>
+-----+------+-----+
| Bal | Cost | Rno |
+-----+------+-----+
| 172 | 0 | 1 |
| 162 | 10 | 2 |
| 150 | 12 | 3 |
| 138 | 12 | 4 |
| 128 | 10 | 5 |
| 113 | 15 | 6 |
+-----+------+-----+
您必须稍微发布您的列,才能将此功能纳入您的触发器。
答案 1 :(得分:0)
我认为你可以尝试触发鳍。
您可以使用IDENT_CURRENT('Table'))从表中获取最后一个主键并进行选择。
我认为这比“选择前1”更好。
要取最后一个余额值,设置变量last_bal =从FINS中选择bal,其中primary_key = Ident_Current(“FINS”)
答案 2 :(得分:0)
好吧
第一个sql是一个游戏,它可以与群组一起工作,或者更确切地说是“设置”,所以你总是考虑到这一点。
如果你使用一个简单的项目是正确的,它可能是更好的方法
declare @myinsert table(id int identity(1,1), company VArchar(35), ts datetime, cost2 smallmoney, bal smallmoney)
insert into @myinsert(company,ts, cost2, bal)
SELECT CORP, CURRENT_TIMESTAMP, COST,
FROM inserted WHERE COALESCE(COST,0) <> 0
declare @current int
select @current = min(id) from @myinsert
while exists(select * from @myinsert where id = @current)
begin
INSERT INTO dbo.FINS
(COMPANY, TS, COST2, BAL)
SELECT COMPANY, CURRENT_TIMESTAMP, COST,
((SELECT TOP 1 BAL FROM FINS WHERE COMPANY = my.COMPANY ORDER BY TS DESC)- COST)
from @myinsert my where id = @current
select @current = min(id) from @myinsert where id > @current
end
答案 3 :(得分:0)
我没有给你确切的查询。暂时忘记触发器。因为你无法测试你的查询。 我建议使用Output子句。这将至少帮助您构建正确的查询并对其进行测试。 这个查询运行正常,(如果你可以使用合并那么这是最好的。)
Declare @t table
(
BLOC1,BLOC2,BLOC3 ,PRODUCTION ,DELTA --whatever column is require here
)
UPDATE STOR SET
STOR.BLOC1 = T.BLOC1,
STOR.BLOC2 = T.BLOC2,
STOR.BLOC3 = T.BLOC3,
STOR.PRODUCTION = T.PROD,
STOR.DELTA = T.DELTA
Output inserted.BLOC1 ,inserted.BLOC2, and so on into @t
FROM BLDG B INNER JOIN STOR S
ON S.B_ID = B.B_ID
CROSS APPLY dbo.INVENTORIZE(B.B_ID) AS T;
现在您已在表变量@t
中插入值 SELECT CORP, CURRENT_TIMESTAMP, COST,
BAL,Row_Number() over(partition by company order by TS desc) RN
FROM @t inner join FINS on COMPANY = CORP
WHERE COALESCE(COST,0) <> 0
在此处验证此查询。稍后优化或触发的思考。 我想我提出了很好的建议。我猜减法不是问题。我告诉将所有内容放在输出子句中并分析查询并测试它。
你也可以使用CTE内部触发器,但你将如何测试呢。
;With CTE as
(
SELECT CORP, CURRENT_TIMESTAMP, COST,BAL
ROW_NUMBER()over(ORDER BY TS DESC )rn
FROM inserted
inner join FINS on COMPANY = CORP
WHERE COALESCE(COST,0) <> 0
)
select * from CTE --check this what you are getting
答案 4 :(得分:0)
类似的东西,不完整。
CREATE TRIGGER trgrCYCLE
ON STOR
FOR UPDATE
AS
begin
declare @last_bal int
declare @company varchar(50)
declare @ts --type
declare @cost int
declare @bal --type
--etc whatever you need
select @company = company, @ts= ts , @cost = cost , @bal = bal from INSERTED
--others selects and sets
set @last_bal = select bal from dbo.FINS where you_primary_key = IDENT_CURRENT('FINS'))
set @last_bal = @last_bal - @cost
Insert INTO FINS (company, ts, cost2, bal) VALUES (@company, @ts, @cost, @last_bal) where --your conditions
end
答案 5 :(得分:0)
如果类似于@ Shantanu的方法,您可以将序列与插入关联,与触发器关联的虚拟表可以通过减去当前记录之前的所有COST来实现。
这可以通过向STOR添加一个rowversion来实现,每次删除都会自动更新。
然后代替:
((SELECT TOP 1 BAL FROM FINS WHERE COMPANY = CORP ORDER BY TS DESC)- COST)
from inserted ...
制作rowversion RV,并且:
(SELECT SUM(X.B) FROM
(SELECT TOP 1 BAL B
FROM FINS
WHERE COMPANY = CORP
ORDER BY TS DESC
UNION
SELECT -COST B
FROM inserted ii
WHERE ii.RV >= i.RV AND ii.CORP = i.CORP
) AS X)
FROM inserted i WHERE COALESCE(COST,0) <> 0
应该做你想做的事。你可以想象一下,这个时间戳比CURRENT_TIMESTAMP的查找粒度更大,我相信它只会下降到秒,但这需要你在UPDATE语句中更新它。 rowversion可能会导致STOR插入语句出现问题。