0

私はカーソルルートに行き、SQLでカーソルを使用することで得られるパフォーマンスの低下を嫌うので、これを行うためのより効率的な方法を見つけようとしています。価値のあるアイテム間で支払いを分配し、残りの金額を追跡しようとしています。例えば...

Payments             
--------
10,
20

Items
------
5,
5,
10

本質的には、最初の支払い (10) が最初の 2 つのアイテムに適用され、使い果たされたことを返します。2 番目の支払い (20) が 3 番目のアイテムに適用され、残りは 10 でした。カーソルを使用して、この問題を解決できます。これをより効率的に行う方法について誰かが考えを持っているかどうか、ただ興味があります.

乾杯!

4

4 に答える 4

1

支払いとアイテムの順序付けられた一連の ID があると仮定すると、支払いに対するアイテムのマッピング テーブルを作成し、次のようなことを行うことができます ( http://sqlfiddle.com/#!3/4b6f8/4を参照)。

-- Populate mapping table
INSERT INTO PaymentsForItems (ItemID, PaymentID)
SELECT ItemID,
       (SELECT MIN(PaymentID)
        FROM Payments p1
        WHERE (SELECT SUM(ItemValue)
               FROM Items i2
               WHERE i2.ItemID <= i1.ItemID) <=
              (SELECT SUM(PaymentValue)
               FROM Payments p2
               WHERE p2.PaymentID <= p1.PaymentID))
FROM Items i1;

これは残りを示していません - これをどのように表現したいかはわかりませんが、簡単に行を個別に挿入できます (たとえば、ID として NULL を使用)。

于 2012-09-05T12:53:38.190 に答える
0

カーソルの代わりに、すべてのアイテムと支払いを取得して、コードでそれらを処理してみませんか?

それはより速いかもしれません、あなたはこれをする2つの方法をベンチに置く必要があるでしょう。

Javaの場合:

PreparedStatement psItems = con.prepareStatement("SELECT itemId,item FROM items");
PreparedStatement psPayments = con.prepareStatement("SELECT payId,payment FROM payments");
ResultSet rsItems = psItems.executeQuery();
ResultSet rsPayments = psPayments.executeQuery();

int currPaymentLeft = 0;
int currentPayId = 0;
int currentItemId = 0;

while(rsItems.next()) {
    int priceItem = rsItems.getInt("item");
    currentItemId = rsItems.getInt("itemId");
    if (currPayment < priceItem) {
        // Here we are in the case where the last payment was over the items it bought.
        // Except if currPayment is 0. In this case, there is no rest.
        if (currPayment > 0) {
            // This is the rest case
            // In currentPayId, we have the Id of a Payment 
            // where there is currPayment left which will be unused.
            // Do whatever you want with this Id and amount.
        }

        currPayment = 0;
        while(rsPayments.next()) {
            currPaymentLeft = rsItems.getInt("payment");
            int payId = rsItems.getInt("payId");
            if(payment >= priceItem) {
                currPaymentLeft -= priceItem;
                // Link currentPayId with currentItemId
                // Create a query, store it in a collection, wo what you want.

                // Get out of this while loop
                break;
            }
            // else, the payment is less than the priceItem
            // so this payment won't be of any use.
            // This is a total rest case
            // In currentPayId, we have the Id of a Payment 
            // where there is currPayment left which will be unused.
            // Do whatever you want with this Id and amount.
        }
    }
    else {
        currPaymentLeft -= priceItem;
        // Link currentPayId with currentItemId because itemId was paid with currentPayId
    }
}
于 2012-09-05T12:25:44.557 に答える
0

このコードを SQL Server 2005 でテストしたところ、問題ないようでした。それがいくつかの助けになることを願っています。

WITH tItems
AS
(
SELECT A.ItemId, A.ItemValue, (SELECT COALESCE(SUM(B.ItemValue), 0)
                               FROM Items B
                               WHERE B.ItemId < A.ItemId) AS PrevItemTotal,

                               (SELECT COALESCE(SUM(C.ItemValue), 0)
                                FROM Items C
                                WHERE C.ItemId <= A.ItemId) AS CurrItemTotal
FROM Items A
),
tPayments
AS
(
SELECT A.PaymentId, A.PaymentValue, (SELECT COALESCE(SUM(B.PaymentValue), 0)
                                     FROM Payments B
                                     WHERE B.PaymentId < A.PaymentId) AS PrevPaymentTotal,

                                    (SELECT COALESCE(SUM(C.PaymentValue), 0)
                                     FROM Payments C
                                     WHERE C.PaymentId <= A.PaymentId) AS CurrPaymentTotal
FROM Payments A
),
tDistribution
AS
(
SELECT *,
CASE
    WHEN PrevPaymentTotal - PrevItemTotal <= 0 THEN
        CASE
            WHEN PaymentValue - (PrevItemTotal-PrevPaymentTotal) <= ItemValue THEN PaymentValue - (PrevItemTotal-PrevPaymentTotal)
            ELSE ItemValue
        END
    ELSE -- PrevPaymentTotal - PrevItemTotal > 0
        CASE
            WHEN ItemValue - (PrevPaymentTotal - PrevItemTotal) < PaymentValue THEN ItemValue - (PrevPaymentTotal - PrevItemTotal)
            ELSE PaymentValue
        END
END AS Distribution
FROM tItems X, tPayments Y
WHERE Y.CurrPaymentTotal > X.PrevItemTotal AND Y.PrevPaymentTotal < X.CurrItemTotal
)

SELECT ItemId, ItemValue, PaymentId, PaymentValue, Distribution,
ItemValue - SUM (Distribution) OVER (PARTITION BY ItemId) AS Remaining
FROM tDistribution
于 2014-11-06T10:04:35.207 に答える
0

私が理解しているように、2 つの異なるが類似したイベントがあります。新しい支払いの受け取りと、新しい費用項目の作成です。それぞれの合流点で、他のテーブルを更新する必要があるため、挿入メカニズムに応じて、ストアド プロシージャまたはトリガーのいずれかが必要になるようです。

新しいアイテムが作成されると、未払い金のある最も早い支払いを見つけて、アイテムのコストだけそれを減らします。

同様に、新しい支払いが作成されると、金額 ( @amount) を順番に分配します。

update items
set 
    paid = 
         case when @amount>=runningTotal then items.amount 
         else 
            case when @amount - (runningTotal - (amount-paid))>0 
                then @amount - (runningTotal - (amount-paid)) 
                else 0 
            end
         end        
from 
    items 
cross apply 
    (
        select sum(amount-paid) as runningTotal 
        from items
        where id <= items.id
    ) as rt 

(もちろん、アトミック トランザクションを使用します)

于 2012-09-05T13:22:53.660 に答える