科目余额表和明细账明细和科目余额表和明细账不同

随笔- 281&
评论- 869&
&&&&&&&&&&&
在开发ERP应用中,我们经常需要知道某个实体的当前数量,例如知道商品当前的库存,或者科目的金额,或者某个客户剩余的信用额度,所以这种需求是比较普遍的。
通常会设计两张表,一张是流水账表,有的称明细表,或者日志表,用于记录所有发生的事务记录。一张是余额表,用于记录各个实体当前最新的余额。
如果有一张单据分别出货P1和P2各一件,而另外一个单据也出货P2和P1各一件,只是顺序不同而已。所以可能出现下面的sql执行顺序。
begin tran
begin tran
INSERT INTO [Chronological]
[ProductId],[Reduce])
(4, &P1&, 1);
INSERT INTO [Chronological]
[ProductId],[Reduce])
VALUES (5, &P2&, 1);
[Banlance]
Set Banlance = Banlance- 1
Where ProductId = &P1&
Update [Banlance]
Set Banlance =
Banlance- 1
Where ProductId =
[Banlance]
Set Banlance = Banlance- 1
Where ProductId = &P2&
Update [Banlance]
Set Banlance =
Banlance- 1
Where ProductId =
可以看出,当两个用户需要的资源(余额表)很容易产生死锁,这是本文要关键解决的问题。
另外,实际情况还要更复杂,由于不能保证余额表一定存在对应的产品记录,当Update Banlance表未影响任何行时,需要执行一个Insert 指令,你很容易想到的,另外一个用户也正好执行了此产品的Update,也发现未影响任何行兵执行一个Insert指令,而后执行的insert将出现&数据重复&的异常。
解决方案一:顺序的更新数据
第一种解决方案是在执行Update指令时,对要更新的数据进行排序。对于此案例,我们可以对ProductId进行排序,重新执行上面的SQL.
begin tran
begin tran
INSERT INTO [Chronological]
[ProductId],[Reduce])
(4, &P1&, 1);
INSERT INTO [Chronological]
[ProductId],[Reduce])
VALUES (5, &P1&, 1);
[Banlance]
Set Banlance = Banlance- 1
Where ProductId = &P1&
Update [Banlance]
Set Banlance =
Banlance- 1
Where ProductId =
[Banlance]
Set Banlance = Banlance- 1
Where ProductId = &P2&
-- 指令 4 & 开始执行
Update [Banlance]
Set Banlance =
Banlance- 1
Where ProductId =
通过排序,我们很好的避免了死锁问题。
实际情况是可能没有对应的商品行,需要提前插入数据,让我们看看没有P1的情况。(为简化示例,省去流水账的SQL,改出库为入库,仅入库P1)。
begin tran
begin tran
[Banlance]
Set Banlance = Banlance + 1
Where ProductId =
-- 没有影响行
Update [Banlance]
Set Banlance =
Banlance + 1
Where ProductId =
-- 没有影响行
Insert Into [Banlance]
(ProductId,Banlance)
Values(&P1&,1);
Insert Into [Banlance]
(ProductId,Banlance)
Values(&P1&,1);
-- 指令&5 开始执行
-- 重复的键
-- 已存在记录,改执行Update
Update [Banlance]
Set Banlance =
Banlance + 1
Where ProductId =
由于第二个insert语句插入相同主键的数据,所以出现等待,当用户1提交后,可以发现用户2执行的insert会失败,改执行update语句。这样也能解决问题,只是非常蹩脚。
如果你使用SQL Server 2008及其以后的版本,可以使用新的语法解决这个问题。
begin tran
begin tran
MERGE [Banlance]
USING (SELECT &P1&) AS source (ProductId)
ON (target.ProductId = source.ProductId)
WHEN MATCHED THEN
UPDATE SET Banlance = Banlance + 1
WHEN NOT MATCHED THEN
INSERT (ProductId, Banlance)
VALUES (&P1&,1);
MERGE [Banlance] AS target
USING (SELECT
&P1&) AS source (ProductId)
(target.ProductId = source.ProductId)
WHEN MATCHED
UPDATE SET Banlance
= Banlance + 1
MATCHED THEN
INSERT (ProductId, Banlance)
VALUES (&P1&,1);
第一个用户会插入数据,第二个用户会执行更新操作。
解决方案二:废弃余额表
第二个方案直接废除余额表,这样就从根本上避免了资源的死锁问题。当然,没有了余额表,要获知当前余额,就需要稍微改动现在的流水表。
创建表的SQL如下:
CREATE TABLE [dbo].[Chronological] (
NVARCHAR (20) NOT NULL,
[ProductId] INT
DECIMAL (18)
DECIMAL (18)
PRIMARY KEY CLUSTERED ([NO] ASC)
通过将相同商品编号的数据分组并sum,即可获取其剩余的库存,例如获取P1的库存。
select Top 1 ProductId,sum([Add]) - sum([Reduce]) from Chronological where ProductId = &P1& and Period = 2
group by ProductI
这里我们使用了区间(Period)来减少数据汇总对应的计算量。来源(Source)如果是空,那么表示此行是期初数据,他在结转期间时创建。
如果你觉得这样做还不够,也可以设计一张流水历史表,将所有非当前区间的数据放在流水历史表,这样当前表(Chronological)的数据量就比较小了,也不要区间(Period)字段了。
另外,如果你加入【发生日期】字段,将达到另外一个好处,可以查询任意时间点的库存了,而不必像之前余额表那样仅能查询当前库存。
最后,如果你希望出库时不允许负库存怎么办?由于Insert执行不能进行检查,再下一条SQL会不会造成查询的库存错误的统计到别人尚未提交的库存记录呢?其实不必担心,只要在查询库存时仅包括比自己记录小的记录即可。
begin tran
begin tran
insert into [Chronological]
([NO],[Period],[Source],[ProductId],[Add],[Reduce])
INSERTED.[NO]
values(6,2,'S3',&P2&,0,9);
insert into
[Chronological]
([NO],[Period],[Source],[ProductId],[Add],[Reduce])
--OUTPUT INSERTED.[NO]
values(7,2,'S4',&P2&,0,5);
select TOP 1 ProductId,sum([Add]) - sum([Reduce]) from
Chronological
with(NOLOCK)
where ProductId =
&P1& and Period = 2 and [NO] &= 6
select TOP 1 ProductId,sum([Add])
- sum([Reduce]) from Chronological
with(NOLOCK)
where ProductId = &P1& and Period = 2 and
group by ProductI
注意的细节是我在select时加入了 with (NOLOCK),数据库默认是没有启用&已提交快照读&,所以在执行第5句话时会出现wait,从而在执行6后,访问的是最新的结果。但如果数据库启用了&已提交快照读&,那么第5句不会wait,且返回5这个不正确的结果。所以我在sql中加入了with(NOLOCK)保证无论何种情况下都检查正确。
阅读(...) 评论()问:账面价值、账面余额有哪些区别?
答:账面原价一般是指资产的历史成本。
账面余额是指某个会计账户(科目)所有明细账户的余额汇总,即总账户余额。比如权益法核算的长期股权投资,包括成本、损益调整和其他权益变动等明细科目余额。
账面价值是指账面余额减去其备抵科目后的余额。备抵科目,一般是指累计折旧(摊销)、资产减值准备等。
对固定资产来讲:
账面价值=固定资产的原价-计提的减值准备-计提的累计折旧
账面余额=固定资产的账面原价
对无形资产来讲:
账面价值=无形资产的原价-计提的减值准备-累计摊销
账面余额=无形资产的账面原价
对以成本模式进行后续计量的投资性房地产:
账面原价=账面余额
账面价值=账面余额-累计折旧(摊销)-减值准备
对于长期股权投资,:
账面价值=账面余额-计提的减值准备
对于交易性金融资产、可供出售金融资产:
账面价值等于账面余额
【】 责任编辑:泥巴姐
&&&&&&&&&&
实务课程分类为什么账户余额和收支明细余额不同_百度知道
为什么账户余额和收支明细余额不同
我有更好的答案
这个是肯定的。
为您推荐:
其他类似问题
账户余额的相关知识
等待您来回答

我要回帖

更多关于 科目余额明细表 的文章

 

随机推荐