我已经构建了一个会计系统,其中在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。 :
我知道我可以添加触发器来更新每一行,但寻找更好的替代方案,(如果有的话)。
C#中的代码。
回答一些问题和常见答案: 1.考虑到我删除的行下面有一百万行 - 通过LINQ在C#中执行它会更好吗?或者SQL触发器工作得更快? 2.我知道保留每个条目留下的余额是荒谬的,但这就是数据库/软件用户对它的要求。
答案 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();
}
这里的优点是:
缺点是:
由于你有一百万行要更新,上面的内容有点慢和浪费......你将不得不用更少的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
或者如果您根本不愿意使用触发器,则可以在应用程序中创建包含更新的存储过程,然后在删除之前调用它。但是在后者中,您应该在单个事务中执行它们以确保维护数据完整性。