简体   繁体   English

交易金额记录更改后触发更新余额

[英]Trigger to update balance after a transaction amount record has been changed

Tables account and transactions表帐户和交易

Account = {accNumber, type, balance, rate, branchName}
Transaction = {accNumber, transNumber, amount, transDate, description}

I have managed to create trigger to update balance after INSERT我已经设法在插入后创建触发器来更新余额

CREATE TRIGGER TR_Account_Balance
ON Transactions AFTER INSERT
AS
BEGIN
    UPDATE account SET
        balance = ins.newBalance
    FROM (
        SELECT a.accnumber,a.balance + SUM(i.amount) AS newBalance
        FROM Account a
        INNER JOIN inserted i ON i.accNumber = a.accNumber
        GROUP BY a.accNumber, a.balance
    ) AS ins
    WHERE account.accnumber = ins.accnumber
END

Now I need to create a trigger that would change balance accordingly to transaction AFTER UPDATE.现在我需要创建一个触发器,该触发器将根据事务更新后相应地更改余额。

How can I possibly do that?我怎么可能做到这一点?

You should really do this in a normalized fashion, by using a view.您应该通过使用视图以规范化的方式真正做到这一点。 For better performance, you can index it.为了获得更好的性能,您可以对其进行索引。

Indexed views are subject to some restrictions, in particular:索引视图受到一些限制,特别是:

  • No outer joins or applys没有外部连接或应用
  • Must be schema-bound必须是模式绑定的
  • Grouped views need COUNT_BIG and can only use SUM as another aggregate分组视图需要COUNT_BIG并且只能将SUM用作另一个聚合
CREATE VIEW dbo.vAccountBalance
WITH SCHEMABINDING AS

SELECT
  tr.accnumber,
  SUM(tr.amount) AS balance,
  COUNT_BIG(*) AS numTransactions -- this MUST be added
FROM dbo.Transactions tr;  -- schema-qualify

GO
CREATE UNIQUE CLUSTERED INDEX CX_vAccountBalance ON dbo.vAccountBalance (accnumber);

The server will maintain this index together with other indexes, during any insert, update or delete.在任何插入、更新或删除期间,服务器将与其他索引一起维护此索引。


If you really wanted to do this in a trigger, you can use the following如果您真的想在触发器中执行此操作,可以使用以下

  • Note how the Account table is only referenced once, and the difference is added, rather than self-joining again注意Account表是如何只引用一次的,加了区别,而不是再次自加入
  • Note how inserted and deleted are joined together by primary key, and the difference is summed注意inserteddeleted是如何通过主键连接在一起的,差值相加
CREATE TRIGGER TR_Account_Balance
ON Transactions AFTER INSERT, UPDATE, DELETE
AS

SET NOCOUNT ON;

IF NOT EXISTS (SELECT 1 FROM inserted) AND NOT EXISTS (SELECT 1 FROM deleted)
    RETURN;  -- early bail-out

UPDATE a  -- update the already referenced Account table
SET
    balance += ins.diffBalance
FROM Account a
INNER JOIN (
    SELECT
      i.accnumber,
      SUM(i.amount) AS diffBalance
    FROM (
        SELECT i.transNumber, i.accnumber, i.amount
        FROM inserted i
    )
    FULL JOIN (
        SELECT d.transNumber, d.accnumber, -(d.amount)
        FROM deleted d
    ) ON i.transNumber = a.transNumber
    GROUP BY i.accNumber
) AS ins ON a.accnumber = ins.accnumber;

GO

You could also split this up into separate INSERT UPDATE and DELETE triggers, in which case you can remove the deleted section for the former, remove the inserted section for the latter, and change the UPDATE one to use an INNER JOIN instead of a FULL JOIN .您还可以将其拆分为单独的INSERT UPDATEDELETE触发器,在这种情况下,您可以删除前者的deleted部分,删除后者的inserted部分,并将UPDATE更改为使用INNER JOIN而不是FULL JOIN .

If you do the insert update delete via a proc that will be the best place to update the mapped table or other tables as well.如果您通过 proc 执行插入更新删除,这将是更新映射表或其他表的最佳位置。 If you still want to do it in a trigger (Carefully) please compute your SUM at same table level and update the balance on main table so it'll cover update and delete as well.如果您仍想在触发器中执行此操作(仔细),请在同一表级别计算您的 SUM 并更新主表上的余额,以便它也涵盖更新和删除。

Schema:架构:

 DROP TABLE IF EXISTS dbo.AccountTransaction
 DROP TABLE IF EXISTS dbo.Account

CREATE TABLE dbo.Account
(
    AccountNumber INT            CONSTRAINT PK_AccountId PRIMARY KEY CLUSTERED IDENTITY(1, 1) NOT NULL,
    Balance       DECIMAL(18, 9) CONSTRAINT DF_Account_Balance DEFAULT 0.0 NOT NULL
)
GO

INSERT INTO dbo.Account
(
    Balance
)
VALUES
(
    DEFAULT -- decimal(18, 9)
)

CREATE TABLE dbo.AccountTransaction
(
    AccountTransactionId INT            CONSTRAINT PK_AccountTransactionId PRIMARY KEY CLUSTERED IDENTITY(1, 1) NOT NULL,
    AccountNumber        INT            CONSTRAINT FK_AccountTransaction_Account FOREIGN KEY REFERENCES dbo.Account (AccountNumber) NOT NULL,
    Amount               DECIMAL(18, 9) CONSTRAINT DF_AccountTransaction_Amount DEFAULT 0.0 NOT NULL
)
GO

CREATE TRIGGER dbo.tr_AccountTransaction
ON dbo.AccountTransaction
FOR INSERT, UPDATE, DELETE
AS
BEGIN
    SET NOCOUNT ON;
     
        UPDATE dbo.Account
        SET    Balance = x.NewBalance
        FROM
               (
                   SELECT   SUM (at.Amount) AS NewBalance
                   FROM     Inserted AS i
                   JOIN     dbo.AccountTransaction AS at
                       ON   at.AccountNumber = i.AccountNumber
                   GROUP BY i.AccountNumber
               ) AS x
    
END
GO

Testing:测试:

INSERT INTO dbo.AccountTransaction
(
    AccountNumber,
    Amount
)
SELECT a.AccountNumber,
       12.0
FROM   dbo.Account AS a

SELECT a.AccountNumber,
       a.Balance
FROM   dbo.Account AS a

UPDATE at
SET    at.Amount += 30
FROM   dbo.AccountTransaction AS at
WHERE  at.AccountTransactionId = 1
SELECT a.AccountNumber,
       a.Balance
FROM   dbo.Account AS a 

SELECT * FROM dbo.AccountTransaction AS at

DELETE a
FROM   dbo.AccountTransaction AS a
WHERE  a.AccountTransactionId = 2
SELECT a.AccountNumber,
       a.Balance
FROM   dbo.Account AS a
SELECT * FROM dbo.AccountTransaction AS at

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM