由CROSS APPLY触发时的顺序SQL插入

gre*_*ner 8 t-sql sql-server cross-apply

此过程有几个步骤,这些步骤反映在数据库的各个表中:

生产 - > 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;
Run Code Online (Sandbox Code Playgroud)

上面提供了一个日志表,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
Run Code Online (Sandbox Code Playgroud)

最后,每次更新应该 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
Run Code Online (Sandbox Code Playgroud)

问题出在这一行:

((SELECT TOP 1 BAL FROM FINS WHERE COMPANY = CORP ORDER BY TS DESC)- COST)
Run Code Online (Sandbox Code Playgroud)

这是为了计算帐户的最新余额.但是因为CROSS APPLY将所有INSERTS作为批处理,所以计算是在相同的最后一条记录中完成的,并且我得到了不正确的余额数字.例:

 COST    BALANCE
----------------
          1,000   <-- initial balance
 -150       850
 -220       780   <-- should be 630
Run Code Online (Sandbox Code Playgroud)

解决这个问题的方法是什么?FINS表上的触发器代替平衡计算?

Sha*_*pta 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
Run Code Online (Sandbox Code Playgroud)

对于外部查询中的每个 CORP,将返回相同的 BAL。

(SELECT TOP 1 BAL FROM FINS WHERE COMPANY = CORP ORDER BY TS DESC)
Run Code Online (Sandbox Code Playgroud)

话虽这么说,每次 CORP = 'XYZ' 时,您的内部查询都会被 1000(您在示例中使用的值)替换

   SELECT CORP, CURRENT_TIMESTAMP, COST, (1000- COST)        
    FROM inserted WHERE COALESCE(COST,0) <> 0
Run Code Online (Sandbox Code Playgroud)

现在您的插入语句包含了所有正在插入的记录。所以每条记录的成本都会减去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 |
+-----+------+-----+-------------------------+
Run Code Online (Sandbox Code Playgroud)

从上次运行余额中减去成本的替代逻辑。

--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
Run Code Online (Sandbox Code Playgroud)

输出

+-----+------+-----+
| Bal | Cost | Rno |
+-----+------+-----+
| 172 |    0 |   1 |
| 162 |   10 |   2 |
| 150 |   12 |   3 |
| 138 |   12 |   4 |
| 128 |   10 |   5 |
| 113 |   15 |   6 |
+-----+------+-----+
Run Code Online (Sandbox Code Playgroud)

您必须在推特上发布一点专栏才能将此功能纳入您的触发器中。