请教计费查询在SQL Server 2000
-
09-09-2019 - |
题
我需要解决一个查询的一些建议。我可以在前端应用程序但是解决这个问题,由于设计,我在后端到inplement这一点。我有以下
CREATE TABLE [dbo].[openitems](
[id] [varchar](8) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
[type] [char](5) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
[date] [smalldatetime] NULL,
[amount] [decimal](9, 2) NULL,
[daysOpen] [smallint] NULL,
[balance] [decimal](9, 2) NULL
) ON [PRIMARY]
insert into openitems values('A12399','INV','2008-12-05',491.96,123)
insert into openitems values('A12399','INV','2008-12-12',4911.37,116)
insert into openitems values('A12399','INV','2008-12-05',3457.69,109)
上面的表具有用于客户的所有开放发票。我需要申请(表daysOpen列)支付从最早的发票开始这些发票。所以,如果我有一个$ 550.00的支付,我将首先把它应用到123 daysOld发票,这是$ 491.96 - $ 500(这让$ 8.04被应用于下一个发票...等),然后更新记录(平衡在表列)为0.00,并移动到下一个和应用剩余。这将是$ 4911.37 - $ 8.04这会使$ 4903.33。由于没有留下任何的平衡被施加,退出循环。
在平衡柱现在应该读
0 4903.33 3457.69
请注意:我需要在表格中做到这一点为所有客户(约10,000)。一位顾客具有开放平均约20发票。
由于
解决方案
考虑如下:
付款要么完全适用于一个平衡,适用于部分的平衡,或高价从平衡。
现在,想象一下,我们可以发现,任何平衡,发票日期的累计余额。而不是想象,让我们做吧:
create view cumulative_balance as
select a.*,
(select sum( balance )
from openitems b
where b.id = a.id and b.type = a.type and a.daysOpen >= a.daysOpen)
as cumulative_balance
from openitems a;
现在,我们可以找到第一个累计余额小于或等于支付,对于任何标识和类型,并加以储存,并daysOpen,并在服务器变量累计余额。
然后,我们更新具有该ID和类型,其中daysOpen <=我们得到的值,所有这些余额设置成零。
所有openItems随后,我们发现标识和类型的第一个非零平衡,并设置其资产负债是它的平衡 - (付款 - 我们存储的累计余额)。如果有一个多付,这种平衡将是正确负。
使用正确的查询,你就可以做的查找和第一次更新在一条语句。
有两个问题。其中之一是,你不能确定两个或两个以上alances使用相同的ID和类型,daysOpen,应先支付的。添加的唯一的ID给您的表将作为一个决胜的这些情况。
其次是需要保存累积余额使用它在查询用于第二更新。如果你正确地设计你的餐桌,与INVOICE_AMOUNT列不是由支付更新和支付列,这是,这将解决您的问题。
这是更好的重构将有两个表,一个是发票和一个用于支付:那么视图可以只完成所有的工作,通过比较累积余额累计支付,生产未付余额或超额支付的列表
其实,我只是设计这样一个系统的一个主要的抵押担保公司,英文缩写FM。这是一个比较复杂一点比你所拥有的,在余额从一数量和百分比,以及多个付款人的公式计算(实际上,保险公司,这对于已经进入默认的抵押贷款)必须在开具发票根据其他规则,每defauted抵押规定顺序。
所有这一切都在做的意见,用短(100行左右)存储过程,基本上做了什么,我已经概述上面:使用通过这些规则有序发票的开票视图,用于支付(在视图),计算哪些额外支付什么日期到保险公司发票。存储过程然后当前日期刚刚生成的发票(其当前日期可以被设置,再次使用的图,以任何日期用于测试目的)。
(具有讽刺意味的是,我已经采取onteh承诺作业我得到写C ++;唯一的C ++我写使用的Oracle和SybaseÇ的API将数据从Oracle系统转移到Sybase之一。)
其他提示
此应该这样做。我声明了一个局部变量,但你可以做,从一个存储过程的参数。我还添加了一个INVOICE_ID表中唯一标识发票,因为ID和日期似乎并不唯一。
DECLARE
@paid_amount DECIMAL(9, 2)
SET @paid_amount = 500
UPDATE
OI
SET
balance =
CASE
WHEN @paid_amount - SQ.running_total > balance THEN 0
ELSE balance - (@paid_amount - SQ.running_total)
END
FROM
dbo.OpenItems OI
INNER JOIN (
SELECT
I1.id,
I1.invoice_id,
I1.date,
ISNULL(SUM(I2.amount), 0) AS running_total
FROM
OpenItems I1
LEFT OUTER JOIN OpenItems I2 ON
I2.id = I1.id AND
I2.type = 'INV' AND
I2.daysopen > I1.daysopen
GROUP BY
I1.id,
I1.invoice_id,
I1.date
) AS SQ ON
SQ.id = OI.id AND
SQ.invoice_id = OI.invoice_id
WHERE
@paid_amount > SQ.running_total
除非这是一个一次性的工作......
听起来好像这是楼内设有商务逻辑和在应用程序的业务层所属。
您必须使用一对夫妇光标和两个嵌套的循环。 (这可能是有点慢)
一读取所有付款 - 我假定客户,金额
然后,对于每个客户创建另一个光标用于打开的项目。
在第一循环将读付款,直到完成
在这个循环中打开一个新的光标,为客户的最旧第一排序开放的项目。
通过每个打开的项目环路和像你描述应用该付款
然后得到下一笔款项。
重复,直到没有更多的付款。