web-dev-qa-db-fra.com

Calculer l'équilibre pour chaque ligne en soustrayant séquentiellement la quantité de chaque ligne d'une somme initiale

Je vais expliquer le problème avec un exemple.

Une requête qui sélectionnera et montrent le processus de retrait que j'ai fabriqué et montrez le statut de mon solde de dépôt.

Une table Deposit est créée avec des colonnes TotalAmount, DepositDate.

Une autre table Withdrawal est créée avec des colonnes WithdrawAmount, WithdrawDate.

J'utilise donc une requête sélectionnée pour sélectionner les deux tables avec des formules:

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

Donc, pour cette requête, supposons que j'ai un acompte de 500 et ne se déposera qu'une seule fois. Cela fonctionne bien si je ne retire qu'une seule fois ce mois-ci et retire complètement le dépôt. Le résultat montrera comme ceci:

| WithdrawAmount | Status          |
| 500            | ZeroBalanceOops |

Cependant, cela ne fonctionne pas si je retire plus d'une fois par mois et que ces retraits feraient la balance = 0. En supposant que le dépôt soit de 500, le résultat montrerait comme ceci:

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

Le résultat attendu est d'avoir "stillavailableyay" au premier sevrage, puis "Zerobalanceoops" au deuxième état de sevrage, mais il ne compare que 500-250 Non 500-250-250 Ce sera toujours le statut "Stillavailableyay". À l'aide de SUM(WithdrawalAmount) ne donne pas le résultat que je veux, car cela ferait que les deux retraits disposent de "Zerobalanceoops".

Une idée de la manière dont puis-je récupérer la requête précédemment sélectionnée et l'inclure dans le calcul? Ou y a-t-il un meilleur moyen de le faire?

2
JamesYTL

Il s'agit d'un type de problème "total d'exécution": le total de chaque ligne est calculé sur la base de la valeur ajoutée de cette ligne ou soustraite du total de la ligne précédente.

Depuis que vous utilisez SQL Server 2014, Transact-SQL possède une syntaxe intégrée à votre disposition pour vous aider à obtenir les résultats.

Utilisation du modèle simplifié de "Un dépôt par mois, de nombreux retraits par mois", la déclaration SQL pourrait aller comme ceci:

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
;

La clause WHERE est censée filtrer les tables à une ligne de dépôt de Deposit et de nombreux retraits correspondants de Withdrawal. Si les tableaux prennent en charge plusieurs comptes et que les montants doivent être davantage liés par compte, vous voudrez peut-être remplacer la clause de quelque chose comme ceci:

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) expression calcule une exécution WithdrawAmount total pour chaque ligne. Donc, le total augmente avec chaque ligne (triés dans l'ordre croissant de WithdrawDate) - ainsi que chaque ligne, une quantité toujours croissante est soustraite de TotalAmount, dessin Balance plus proche de 0.

La requête ci-dessus vous donnera l'équilibre de chaque ligne mais pas le statut. Pour obtenir le statut, vous devrez référencer la valeur Balance pour la comparer à 0 et sélectionner une chaîne d'état correspondante à revenir. Balance est une colonne calculée et afin de pouvoir y référoir, vous devez nier la requête et la référence ci-dessus Balance au niveau extérieur. La nidification pourrait être faite avec une table dérivée ou une expression de table commune (CTE). Cette requête utilise un 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
;

Bien sûr, si vous n'avez pas besoin de retourner la balance - uniquement pour le comparer à 0, la nidification n'est pas nécessaire et vous pouvez mettre la fonction d.TotalAmount - SUM(w.WithdrawAmount) OVER (ORDER BY w.WithdrawDate ASC) expression directement dans le boîtier, remplaçant Balance.

4
Andriy M