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 の請求書を開いています。

ありがとう

4

4 に答える 4

2

次の点を考慮してください。

支払いは、残高に全額適用されるか、残高に一部適用されるか、または残高を過払いします。

ここで、任意の残高について、現在までの請求書の累積残高を見つけることができると想像してください。想像するより、やってみましょう。

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 とタイプのゼロ以外の最初の残高を見つけ、その残高をその残高 (支払い - 保存した累積残高) に設定します。過払いがある場合、この残高は正しくマイナスになります。

正しいクエリを使用すると、ルックアップと最初の更新を 1 つのステートメントで実行できます。

2 つの問題があります。1 つは、同じ ID、タイプ、daysOpen を持つ 2 つ以上のアランスのうち、どちらを先に支払うべきかを判断できないことです。テーブルに一意のID を追加すると、これらのケースのタイブレーカーとして機能します。

2 つ目は、2 回目の更新のクエリで使用するために累積残高を保存する必要があることです。支払いによって更新されなかったinvoice_amountの列と、更新された支払い列を使用して、テーブルを正しく設計した場合、これで問題が解決します。

さらに優れたリファクタリングは、請求書用と支払い用の 2 つのテーブルを用意することです。ビューは、累積残高と累積支払いを比較し、未払い残高または過払いのリストを生成することによって、すべての作業を行うことができます。

実際、私はイニシャルが FM の大手住宅ローン保証会社のために、まさにそのようなシステムを設計しました。残高は金額とパーセンテージの多数の式から計算され、複数の支払人 (実際には保険会社、これは債務不履行になった住宅ローンの場合) に請求する必要があったという点で、あなたが持っているものよりも少し複雑でした。無効な住宅ローンごとに、他の規則に従って所定の順序。

これらはすべてビューで行われ、短い (100 行ほどの) ストアド プロシージャを使用して、上で概説したことを本質的に実行しました。どの保険会社に、どの日付にどの追加支払いを請求するかを計算します。ストアド プロシージャは、現在の日付の請求書を生成しました (現在の日付は、テスト目的で、ビューを使用して任意の日付に設定できます)。

(皮肉なことに、私は C++ を書くようになるという約束でその仕事を引き受けました。私が書いた唯一の C++ は、Oracle システムから Sybase システムにデータを転送するために Oracle および Sybase C API を使用していました。)

于 2009-04-09T20:39:33.327 に答える
1

これでうまくいくはずです。ローカル変数を宣言しましたが、それをストアド プロシージャのパラメーターにすることができます。また、ID と日付が一意ではないように見えるため、請求書を一意に識別するために、invoice_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
于 2009-04-09T22:21:20.540 に答える
0

これが一度限りの努力でない限り...

これはビジネス ロジックであり、アプリケーションのビジネス レイヤーに属しているようです。

于 2009-04-09T20:19:07.837 に答える
-2

2 つのカーソルと 2 つのネストされたループを使用する必要があります。(これは少し遅いかもしれません)

すべての支払いを読み取るための1つ-私は顧客、金額を想定しています

次に、顧客ごとに未決済明細用の別のカーソルを作成します。

最初のループは、完了するまで支払いを読み取ります

そのループ内で、古い順に並べ替えられた顧客の未処理アイテムの新しいカーソルを開きます。

開いている各アイテムをループし、説明に従って支払いを適用します

その後、次の支払いを受け取ります。

支払いがなくなるまで繰り返します。

于 2009-04-09T19:31:45.893 に答える