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

标签 sql-server tsql cross-apply

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

生产 --> 更新到库存表,使用类似

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 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 表上使用触发器来代替余额计算?

最佳答案

了解查询中的现有逻辑

UPDATE 语句只会针对满足连接条件的集合或批处理触发一次触发器,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

现在您的插入语句包含了正在插入的所有记录。因此每条记录的成本都会减去 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 |
+-----+------+-----+

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

关于sql-server - 由 CROSS APPLY 触发时的顺序 SQL 插入,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21893462/

相关文章:

sql - T-SQL : How to select one column or in alternative another one?

sql-server-2005 - 使用 CTE 代替 Cursor

sql-server - 交叉应用与 "comma Join"

分区上的 SQL Sum

sql-server - 重命名多个表

c# - 我如何从 sql server 的 RAISERROR 捕获 C# 中的错误?

sql - 运行 SELECT 语句时,日期列中的空白值返回为 1900/01/01

sql-server - tsql 星期几

MySQL - 1 id 多行并将多列数据转换为逗号分隔行

Linq 转 SQL。如何防止使用Apply运算符