删除SQL中删除行时的所有后续行

时间:2012-12-10 12:58:51

标签: sql sql-server sql-server-2008 accounting

我已经构建了一个会计系统,其中在SQL表中,其中两列是: 金额和余额按以下方式保留:

ID    Amount    Balance Left  
101    20        100  
102    20         80  
103    10         70  
104    30         40  
105    25         15  
106     5         10

这样:Balance Left = previous Balance Left - Amount

现在,如果我删除ID为103的行,我希望将金额10添加到所有后续行中。 什么是最好的方法(在SQL Server 2008或其他方面)?

P.S。 :

  1. 我知道我可以添加触发器来更新每一行,但寻找更好的替代方案,(如果有的话)

  2. C#中的代码。

  3. 回答一些问题和常见答案: 1.考虑到我删除的行下面有一百万行 - 通过LINQ在C#中执行它会更好吗?或者SQL触发器工作得更快? 2.我知道保留每个条目留下的余额是荒谬的,但这就是数据库/软件用户对它的要求。

4 个答案:

答案 0 :(得分:5)

最好的方法是不存储余额,而是在需要时在视图中计算。

但是,如果这不可取或不可能,那么触发器是您最好的选择,因为您可以确保您的数据保持一致。实际上,删除对表的所有写访问权以及通过存储过程强制进行更改也很有效,尽管您仅限于一次一行更新&删除。

伪码格式的查询将是 更新MyTable set BalanceLeft = BalanceLeft + RemovedAmount 其中ID> RemovedID

如果您希望此表上有多个读取器/写入器,则还需要考虑锁定并确保删除和后续更新位于同一事务中。如果你让一个视图计算剩余的余额,大多数问题都会消失。

答案 1 :(得分:2)

我不知道“最好的可能”,但是这里是如何在linq to sql中完成的。

using (CustomDataContext dc = new CustomDataContext())
{
  List<Row> rows = dc.Rows
    .Where(row => startingID <= row.ID)
    .OrderBy(row => row.ID)
    .ToList();

  Row firstRow = rows.First();
  int firstRowAmount = firstRow.Amount;
  dc.Rows.DeleteOnSubmit(firstRow);

  foreach(Row row in rows.Skip(1))
  {
    row.Amount -= firstRowAmount;
  }
  dc.SubmitChanges();
}

这里的优点是:

  • 单笔交易 - 整个更新进行或不进行。
  • 乐观并发 - 如果有人在我们的阅读和写作之间删除或更新了我们的某一行,我们的更改将被拒绝。
  • 清除可维护的c#代码。

缺点是:

  • 必须先将记录读入内存才能更新。
  • 每行生成一个更新语句。

由于你有一百万行要更新,上面的内容有点慢和浪费......你将不得不用更少的sql命令更新这些行,而不必将行读入内存。

using(TransactionScope ts = new TransactionScope())
{
  using (CustomDataContext dc = new CustomDataContext())
  {
    Row theRow = dc.Rows.Single(row => startingID == row.ID)

    int firstRowAmount = theRow.Amount;
    dc.Rows.DeleteOnSubmit(theRow);
    dc.SubmitChanges();
    dc.ExecuteCommand(@"Update Row SET Amount = Amount - {0} WHERE {1} < ID", firstRowAmount, startingID);
    ts.Complete();
  }
}

答案 2 :(得分:1)

DECLARE @T TABLE
(
    ID INT,
    Amount INT,
    [Balance Left] INT

)

INSERT INTO @T VALUES
(101,20,100),
(102,20,80),
(103,10,70),
(104,30,40),
(105,25,15),
(106,5,10)

DECLARE @ID_DELETE INT = 103
DECLARE @AMOUNT_DELETED INT = 0

SELECT @AMOUNT_DELETED = Amount
FROM @T WHERE ID = @ID_DELETE

DELETE FROM @T WHERE ID = @ID_DELETE

UPDATE @T SET [Balance Left] = [Balance Left] + @AMOUNT_DELETED WHERE ID > @ID_DELETE

SELECT * FROM @T

答案 3 :(得分:0)

首先,您不需要单独更新每一行,而是可以在删除时触发一次更新所有基础行:

CREATE TRIGGER sampleTrigger
        ON database1.dbo.balanceTable
        FOR DELETE
    AS
      UPDATE balanceTable t
      SET t.BalanceLeft = t.BalanceLeft + deleted.Amount
      Where t.ID > deleted.ID
    GO

或者如果您根本不愿意使用触发器,则可以在应用程序中创建包含更新的存储过程,然后在删除之前调用它。但是在后者中,您应该在单个事务中执行它们以确保维护数据完整性。