J'ai un tableau qui stocke le calendrier des revenus dus, pour un certain nombre d'actifs.
Ce tableau indique la date d'entrée en vigueur d'un nouveau montant de revenu, ainsi que le montant de revenu quotidien.Somme du revenu journalier possible sans curseur?
Je veux calculer le revenu total dû entre 2 dates.
est ici la structure de la table et les échantillons:
DECLARE @incomeschedule
TABLE (asset_no int, start_date datetime, amt decimal(14,2),
PRIMARY KEY (asset_no, start_date))
/*
-- amt is the amount of daily income
-- start_date is the effective date, from when that amt starts to be come in
*/
INSERT INTO @incomeschedule (asset_no, start_date, amt)
VALUES (1, '1 Jan 2010', 3)
INSERT INTO @incomeschedule (asset_no, start_date, amt)
VALUES (1, '1 Jul 2010', 4)
INSERT INTO @incomeschedule (asset_no, start_date, amt)
VALUES (1, '1 Oct 2010', 5)
INSERT INTO @incomeschedule (asset_no, start_date, amt)
VALUES (2, '1 Jan 2010', 1)
INSERT INTO @incomeschedule (asset_no, start_date, amt)
VALUES (2, '1 Jan 2012', 2)
INSERT INTO @incomeschedule (asset_no, start_date, amt)
VALUES (2, '1 Jan 2014', 4)
INSERT INTO @incomeschedule (asset_no, start_date, amt)
VALUES (2, '1 Jan 2016', 5)
Donc, pour l'actif 1, il est revenu $ 3 Tous les jours partir du 1er janvier, passant à 4 $ de 1 juil à 5 $ du 1 octobre
Pour le calcul du revenu total entre le 1er janvier 2010 et le 31 décembre 2020, en utilisant l'actif 1 comme exemple, nous avons
- 181 jours à 3 $ (1er janvier 2010 au 30 juin 2010) = 543 $
- plus 92 jours à 4 $ (du 1er juillet 2010 au 30 sept. 2010) = 368 $
- en plus 3744 jours à 5 $ (1 octobre 2010 à 31 décembre 2020) = $ 18720
- au total 19631
$ [De même, l'actif 2 arrive à 14242 $]
Donc, pour une plage d'entrée Au 1 er janvier 2010 au 31 déc 2020, je me attends à la sortie suivante:
asset_no total_amt
1 19631.00
2 14242.00
Je l'ai écrit en utilisant un curseur [comme je l'ai besoin de connaître les valeurs précédentes des lignes pour effectuer les CALC] mais je voudrais savoir s'il est possible de produire ces résultats en utilisant des techniques basées sur des ensembles.
Voici le code basé sur le curseur, au cas où cela serait utile.
DECLARE @date_from datetime,
@date_to datetime
SET @date_from = '1 Jan 2010'
SET @date_to = '31 Dec 2020'
/*-- output table to store results */
DECLARE @incomeoutput TABLE (asset_no int PRIMARY KEY, total_amt decimal(14,2))
/*-- cursor definition */
DECLARE c CURSOR FAST_FORWARD FOR
SELECT asset_no, start_date, amt
FROM @incomeschedule
UNION
/* insert dummy records to zeroise from @date_from,
in case this is earlier than initial start_date per asset */
SELECT DISTINCT asset_no, @date_from, 0
FROM @incomeschedule
WHERE NOT EXISTS (SELECT asset_no, start_date FROM @incomeschedule WHERE start_date <= @date_from)
ORDER BY asset_no, start_date
/*-- initialise loop variables */
DECLARE @prev_asset_no int, @dummy_no int
SET @dummy_no = -999 /* arbitrary value, used to detect that we're in the first iteration */
SET @prev_asset_no = @dummy_no
DECLARE @prev_date datetime
SET @prev_date = @date_from
DECLARE @prev_amt decimal(14,2)
SET @prev_amt = 0
DECLARE @prev_total decimal(14,2)
SET @prev_total = 0
DECLARE @asset_no int, @start_date datetime, @amt decimal(14,2)
/*-- read values from cursor */
OPEN c
FETCH NEXT FROM c INTO @asset_no, @start_date, @amt
WHILE @@FETCH_STATUS = 0
BEGIN
/*-- determine whether we're looking at a new asset or not */
IF @prev_asset_no = @asset_no -- same asset: increment total and update loop variables
BEGIN
SET @prev_asset_no = @asset_no
SET @prev_total = @prev_total + (@prev_amt * DATEDIFF(d, @prev_date, @start_date))
SET @prev_date = @start_date
SET @prev_amt = @amt
END
ELSE /*-- new asset: output record and reset loop variables */
BEGIN
IF @prev_asset_no <> @dummy_no /*-- first time round, we don't need to output */
BEGIN
SET @prev_total = @prev_total + (@prev_amt * DATEDIFF(d, @prev_date, @date_to))
INSERT INTO @incomeoutput (asset_no, total_amt) VALUES (@prev_asset_no, @prev_total)
END
SET @prev_asset_no = @asset_no
SET @prev_total = 0
SET @prev_date = @start_date
SET @prev_amt = @amt
END
FETCH NEXT FROM c INTO @asset_no, @start_date, @amt
END
SET @prev_total = @prev_total + (@prev_amt * DATEDIFF(d, @prev_date, @date_to))
INSERT INTO @incomeoutput (asset_no, total_amt) VALUES (@prev_asset_no, @prev_total)
CLOSE c
DEALLOCATE c
SELECT asset_no, total_amt
FROM @incomeoutput
n.b. J'ai envisagé de poster la solution basée sur le curseur comme une réponse, pour éviter de gonfler la question ... mais la façon dont j'ai formulé la question J'ai besoin d'une réponse sans curseur, donc c'est comme la meilleure approche. S'il vous plaît commenter si ce n'est pas l'étiquette correcte.
+1 pour l'affichage de DDL, je n'aurais pas répondu sans elle. – RedFilter
@RedFilter - merci beaucoup, je tiens à publier DDL car je ne peux pas vraiment se permettre le Bounty. :) – richaux