web-dev-qa-db-ja.com

最初の合計から各行の金額を順次差し引くことにより、すべての行のバランスを計算します

問題を例を挙げて説明します。

私が行った引き出しプロセスを選択して表示し、預金残高のステータスを表示するクエリ。

DepositTotalAmountを含むテーブルDepositDateが作成されます。

WithdrawalWithdrawAmountを含む別のテーブルWithdrawDateが作成されます。

したがって、SELECTクエリを使用して、式を使用して両方のテーブルから選択します。

_SELECT WithdrawAmount,
CASE WHEN ( TotalAmount - WithdrawAmount) = 0 THEN 'ZeroBalanceOops'
ELSE 'StillAvailableYAY' as 'Status'
FROM Deposit Inner Join Withdraw WHERE [WithdrawDate] between this month beginning and ending
_

したがって、このクエリでは、500のデポジットがあり、一度だけデポジットするとします。今月に一度だけ引き落とし、預金を完全に引き落とすとうまくいきます。結果は次のようになります。

_| WithdrawAmount | Status          |
| 500            | ZeroBalanceOops |
_

ただし、1か月に2回以上引き出した場合は機能しません。これらの引き出しにより残高は0になります。預金が再び500であるとすると、結果は次のようになります。

_| WithdrawAmount  | Status            |
| 250             | StillAvailableYAY |
| 250             | StillAvailableYAY |
_

期待される結果は、最初の引き出しで「StillAvailableYAY」、次に2番目の引き出しステータスで「ZeroBalanceOops」になることですが、比較するのは_500-250_だけではなく_500-250-250_なので、常に「StillAvailableYAY」ステータスになります。 SUM(WithdrawalAmount)を使用しても、2つの引き出しのステータスが「ZeroBalanceOops」になるため、希望する結果が得られません。

以前に選択したクエリを取得して計算に含めるにはどうすればよいですか?それとも、もっと良い方法はありますか?

2
JamesYTL

これは「積算合計」タイプの問題です。各行の合計は、前の行の合計に追加または減算されたその行の値に基づいて計算されます。

SQL Server 2014を使用しているため、Transact-SQLには、結果の取得に役立つ組み込みの構文が用意されています。

「1か月に1回のデポジット、1か月に多くの引き出し」という単純なモデルを使用すると、SQLステートメントは次のようになります。

_SELECT
  d.TotalAmount,
  w.WithdrawAmount,
  Balance = d.TotalAmount - SUM(w.WithdrawAmount) OVER (ORDER BY w.WithdrawDate ASC)
FROM
  dbo.Deposit AS d,
  dbo.Withdrawal AS w
WHERE
  d.DepositDate  >= start_of_this_month AND d.DepositDate  < start_of_next_month
  AND
  w.WithdrawDate >= start_of_this_month AND w.WithdrawDate < start_of_next_month
;_

WHERE句は、Depositからの1つの入金行と、Withdrawalからの対応する多数の引き出しにテーブルをフィルタリングすることになっています。テーブルが複数のアカウントをサポートし、金額をアカウントごとにさらに関連付ける必要がある場合は、FROM句を次のように置き換えることができます。

_FROM
  dbo.Deposit AS d
  INNER JOIN dbo.Withdrawal AS w ON d.AccountNumber = w.AccountNumber
_

SUM(w.WithdrawAmount) OVER (ORDER BY w.WithdrawDate ASC)式は、各行の実行中のWithdrawAmount合計を計算します。したがって、合計は各行で増加します(WithdrawDateの昇順で並べ替えられます)。したがって、各行でTotalAmountから増加する量が減算され、Balanceが0に近づきます。

上記のクエリは、各行の残高を提供しますが、ステータスは提供しません。ステータスを取得するには、Balance値を参照して0と比較し、対応するステータス文字列を選択して返す必要があります。 Balanceは計算列であり、それを参照できるようにするには、上記のクエリをネストして、外部レベルでBalanceを参照する必要があります。ネストは、派生テーブルまたは共通テーブル式(CTE)のいずれかで行うことができます。このクエリはCTEを使用します。

_WITH balances AS
  (
    SELECT
      d.TotalAmount,
      w.WithdrawAmount,
      Balance = d.TotalAmount - SUM(w.WithdrawAmount) OVER (ORDER BY w.WithdrawDate ASC)
    FROM
      dbo.Deposit    AS d,
      dbo.Withdrawal AS w
    WHERE
      d.DepositDate  >= start_of_this_month AND d.DepositDate  < start_of_next_month
      AND
      w.WithdrawDate >= start_of_this_month AND w.WithdrawDate < start_of_next_month
  )
SELECT
  TotalAmount,
  WithdrawAmount,
  Balance,
  Status = CASE WHEN Balance > 0 THEN 'StillAvailableYAY' ELSE 'ZeroBalanceOops' END
FROM
  balances
;_

もちろん、本当に残高を返す必要がない場合、それを0と比較するだけの場合は、入れ子にする必要はなく、d.TotalAmount - SUM(w.WithdrawAmount) OVER (ORDER BY w.WithdrawDate ASC)式をCASEに直接入れて、Balanceを置き換えることができます。

4
Andriy M