我有一張表,用於存儲許多資產的到期收入時間表。
該表格給出了新收入金額生效的日期以及該日收入金額。沒有光標的情況下,日常收入的總和?
我想計算兩個日期之間到期的總收入。
這裏的表結構和樣本數據:
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)
因此,對於資產1,有$ 3收入每天從1月1日,從1上升到$ 4從7月1日,5 $十月
爲計算2010年1月1日至2020年12月31日期間的總收入,以資產1爲例,我們有
- 181天爲3美元(2010年1月1日至2010年6月30日)= 543美元
- 加上92天4美元(2010年7月1日至2010年9月30日)= 368美元
- 加3744天在$ 5(2010年10月1日至12月31日2020年)= $一萬八千七百二十
- 總計$一九六三一
所以[同樣地,資產2爲$ 14242進來] 2010年1月1日至12月31日的輸入範圍2020年,我希望以下的輸出:
asset_no total_amt
1 19631.00
2 14242.00
我一直在使用光標[我需要知道以前的行值執行Calcs(計算)],但我想知道是否有可能產生這樣寫這些結果使用基於集合的技術。
這是基於光標的代碼,以防萬一。
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.我確實考慮過發佈基於光標的解決方案作爲答案,以避免使問題膨脹......但是,我說出了問題的方式,我需要一個基於非遊標的答案,所以這感覺就像是更好的方法。請評論這是不是正確的禮儀。
+1發佈DDL,我不會沒有它回答它。 – RedFilter 2010-11-16 15:08:00
@RedFilter - 非常感謝,我喜歡發佈DDL,因爲我無法承擔賞金。 :) – richaux 2010-11-16 16:37:06