繁体   English   中英

关于SQL Server 2000中的计费查询的建议

[英]ADVICE on billing Query in SQL Server 2000

在处理查询时,我需要一些建议。 我可以在前端应用程序中进行处理,但是由于设计原因,我必须在后端进行补充。 我有以下


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天的帐单上,即$ 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;

现在,对于任何id和类型,我们都可以找到小于或等于付款额的第一个累计余额,并将其,daysOpen和累计余额存储在服务器变量中。

然后,我们使用该ID和类型更新所有openItems,其中daysOpen <=我们获得的值,将所有余额设置为零。

然后,我们找到该ID和类型的第一个非零余额,并将其余额设置为余额-(付款-我们存储的累计余额)。 如果存在多付款项,则此余额正确为负。

使用正确的查询,您将能够在一个语句中进行查找和首次更新。

有两个问题。 一个是您无法确定具有相同ID,类型和daysOpen的两个或多个查询,应首先付款。 在这些表中添加唯一的 ID将成为平局。

其次是需要保存累积余额以在第二次更新的查询中使用它。 如果您正确地设计了表,并为invoice_amount的列未按付款进行更新,而对付款列进行了付款,则可以解决您的问题。

更好的重构方法是有两个表,一个表用于发票,一个表用于付款:然后,一个视图可以通过比较累计余额与累计付款,生成未付余额或多付款清单来完成所有工作。

实际上,我为一家主要的抵押担保公司设计了这样一个带有缩写FM的系统。 它比您拥有的要复杂一些,因为余额是根据许多金额和百分比公式计算得出的,因此必须在一张发票中开具多个付款人(实际上是保险人,这是针对已违约的抵押贷款)的发票。根据其他规则按规定的顺序,针对每一个被毁坏的抵押贷款。

所有这些操作都是在视图中完成的,其中有一个简短的(大约100行左右)存储过程,基本上完成了我上面概述的操作:使用了一个视图,该视图根据这些规则对发票进行了排序,应用了付款(在视图中),计算在哪个保险公司的哪个日期开具发票的额外付款。 然后,存储过程仅生成了当前日期的发票(可以再次使用视图将当前日期设置为用于测试目的的任何日期)。

(具有讽刺意味的是,我曾保证会写C ++;我写的唯一C ++使用Oracle和Sybase C 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

您将不得不使用几个游标和两个嵌套循环。 (这可能会有点慢)

一看所有的付款-我假设客户,金额

然后为每个客户为未清项目创建另一个光标。

第一个循环将读取付款,直到完成

在该循环中,为客户的未清项目打开一个新的光标,按最旧的顺序排序。

循环浏览每个未清项目并按照您所描述的进行付款

然后获得下一笔付款。

重复直到没有更多付款。

除非这是一次性的努力...

听起来这是商务逻辑,并且属于应用程序的业务层。

暂无
暂无

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

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