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

发布于 2024-07-16 11:09:36 字数 1045 浏览 7 评论 0原文

我需要一些处理查询的建议。 我可以在前端应用程序中处理这个问题,但是,由于设计原因,我必须在后端实现它。 我有以下


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 张未结发票。

谢谢

I need some advice in tackling a query. I can handle this in a front-end application, however, due to design, I have to inplement this in the back-end. I have the following


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)

The table above have all open invoices for a customer. I need to apply a payment to these invoices starting from the oldest invoice (daysOpen column in the table). So if I have a $550.00 payment, I'll first apply it to the invoice with 123 daysOld, that's $491.96 -$500 (which leaves $8.04 to be applied to the next invoice... and so on), then update that record (balance column in table) to 0.00 and move to the next and apply the remaining. That would be $4911.37 - $8.04, which would leave $4903.33. Since there is no balance left to be applied, the loop exits.

The balance column should now read

0
4903.33
3457.69

Note: I need to do this for all customers in a table (around 10,000). A customer has an average of about 20 invoices open.

Thanks

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

蓝色星空 2024-07-23 11:09:36

请考虑以下情况:

付款要么全额应用于余额,部分应用于余额,要么超额支付余额。

现在,想象一下,对于任何余额,我们都可以找到迄今为止发票的累积余额。 不要想象,让我们这样做:

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 的 alance 中哪个应该先支付。 在您的表中添加唯一 id 可以作为这些情况的决胜局。

其次是需要保存累积余额以便在第二次更新的查询中使用它。 如果您正确设计了表格,其中发票金额列未由付款更新,而付款列则为,这将解决您的问题。

更好的重构是有两张表,一张用于发票,一张用于付款:然后一个视图可以完成所有工作,将累积余额与累积付款进行比较,生成未付余额或超额付款的列表。

事实上,我正是为一家大型抵押贷款担保公司(其缩写为 FM)设计了这样一个系统。 它比你所拥有的要复杂一些,因为余额是根据许多金额和百分比的公式计算出来的,并且多个付款人(实际上,保险公司,这是针对已经违约的抵押贷款)必须以发票形式开具发票。根据其他规则,针对每项违约抵押贷款制定规定的顺序。

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

(具有讽刺意味的是,我接受这份工作时承诺我会编写 C++;我编写的唯一 C++ 使用 Oracle 和 Sybase C API 将数据从 Oracle 系统传输到 Sybase 系统。)

Consider the following:

a payment either applies in full to a balance, applies in part to a balance, or overpays a balance.

Now, imagine that we could find, for any balance, the cumulative balance of invoices to date. Rather than imagine that, let's do it:

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;

Now we can find the first cumulative balance less than or equal to the payment, for any id and type, and store that, and daysOpen, and cumulative balance in server variables.

Then we update all openItems with that id and type, where daysOpen <= the value we got, setting all those balances to zero.

Then we find the first non-zero balance of that id and type, and set its balance to be it's balance - (payment - the cumulative balance we stored). if there's an overpayment, this balance will be correctly negative.

With the correct query, you'll be able to do the lookup and first update in one statement.

There are two problems. One is that you can't determine, of two or more alances with the same id and type and daysOpen, which should be paid first. Adding a unique id to your table would serve as a tie-breaker for those cases.

Second is the need to save the cumulative balance to use it in the query for the second update. if you designed your table correctly, with a column for invoice_amount that wasn't updated by payments, and a payment column that was, this would solve your problem.

An even better refactoring would be to have two tables, one for invoices and one for payment: then a view could just do all the work, by comparing cumulative balances to cumulative payments, producing a list of unpaid balances or overpayments.

In fact, I designed just such a system for a major mortgage guarantee company with the initials FM. It was a bit more complicated than what you have, in that balances were calculated from a number of formulas of amounts and percentages, and multiple payers (actually, insurers, this was for mortgages that had gone into default) had to be invoiced in a prescribed order according to other rules, per defauted mortgage.

All of this was done in views, with a short (100 line or so) stored procedure that essentially did what I've outlined above: used a view that ordered the billing of invoices by these rules, applied payments (in the view), calculating what additional payments to invoice on what date to which insurer. The stored procedure then just generated invoices for the current date (which current date could be set, again using a view, to any date for testing purposes).

(The irony is that I'd taken the job onteh promise I'd get to write C++; the only C++ I wrote used the Oracle and Sybase C APIs to transfer data from the Oracle system to the Sybase one.)

清眉祭 2024-07-23 11:09:36

这应该可以做到。 我已经声明了一个局部变量,但您可以将其作为存储过程的参数。 我还在表中添加了一个发票 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

This should do it. I've declared a local variable, but you could make that a parameter from a stored procedure. I've also added an invoice_id to the table to uniquely identify invoices since id and date don't seem to be unique.

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
护你周全 2024-07-23 11:09:36

除非这是一次性的工作...

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

Unless this is a one time effort...

It sounds like this is buisness logic and belongs in your business layer of your application.

因为看清所以看轻 2024-07-23 11:09:36

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

读取所有付款的一个 - 我假设客户,金额

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

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

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

循环遍历每个未清项目并按照您的描述应用付款,

然后获取下一笔付款。

重复直到不再付款。

You would have to use a couple of cursors and two nested loops. (this may be a little slow)

One to read all the payments - I assume Customer, Amount

Then for each customer create another cursor for the open items.

The first loop will read payments until done

Within that loop open a new cursor for the customer's open items sorted by oldest first.

Loop through each open item and apply the payments as you described

Then get next payment.

Repeat until no more payments.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文