T-SQL 銀行家

來源:互聯網
上載者:User
發布日期: 4/1/2004 | 更新日期: 4/1/2004

針對某些 財務問題的基於集合 (set-based) 的解決方案

Itzik Ben-Gan

最近,在我講授一節 SQL Server 編程課程時,有幾個學生給我提出了一個難題。在他們的生產系統中,使用了各種必須向貨幣值(如存款)應用利率或指數的財務應用程式。這些學生有一個與該系統有關的具體問題,想知道對於這個問題是否有基於集的 T-SQL 解決方案。答案當然是肯定的!讓我們研究一下我的學生所提出的這個一般性問題,以便您可以根據自己的具體需要來調整解決方案。

指數化貨幣值

貨幣價值是隨著時間的推移而發生變化的。財務應用程式很少會以其輸入資料庫時的原始形式來顯示貨幣值的。財務機構需要向儲蓄帳戶存款應用利率、向損益報表中的貨幣值應用指數率(如消費價格指數 - CPI)、向欠外國供應商的債務應用貨幣兌換率等。

通常,貨幣值是與生效日期一起輸入到財務應用程式的資料庫中的。財務應用程式將向本金應用一系列調整,並計算截止到之後某個日期(如“今天”或“2002 年 12 月 31 日)的結果值。例如,假設 2002 年 9 月 7 日您向系統中輸入金額 $1,300。您需要將該金額與 CPI(CPI 於每月的 15 日進行更新)進行連結,並顯示截止到 2002 年 12 月 31 日的值。CPI 包含月利率,這些月利率反映貨幣值根據消費者平均購買力而發生的變化。諸如 CPI 之類的指數能夠以增長百分比或比例係數來表示。消費者通常關心增長百分比,而會計通常使用指數率進行計算。

在本例中,假設指數率在 9 月到 12 月期間分別增長了 0.3%、0.6%、0.4% 和 0.9%。本金金額則需要進行如下調整:

$1,300.0000 * (1 + 0.3%) = $1,303.9000 $1,303.9000 * (1 + 0.6%) = $1,311.7234 $1,311.7234 * (1 + 0.4%) = $1,316.9703 $1,316.9703 * (1 + 0.9%) = $1,328.8230

在向儲蓄帳戶存款應用利率時,將使用類似的計算(假設在帳戶條款中指定的是非固定的利率),但是利率變化的間隔可能會有所不同。

現在,讓我們看一下這個一般性的問題。運行清單 1中的指令碼,建立 Amounts 和 IndexRates 表,然後在其中填入樣本資料。Amounts 表包含貨幣值及其生效日期,IndexRates 表包含 CPI 的月增長百分比。您的任務是編寫一個查詢,讓該查詢能夠通過調整金額來反映將來的一個給定日期(在 @givendate 變數中提供)。例如,第 18 頁上的 表 1顯示了這個特定日期為 2002 年 12 月 31 日時所需的結果。

模仿彙總 PRODUCT() 的解決方案

我們首先寫出要應用於每個金額的公式:

indexed_value =  amt_value * (1+1st idx_growth) * (1+2nd idx_growth) * ... * (1+last idx_growth)

其中 1st、2nd(等)比率分別對應於金額的生效日期和其間的幾個時段,last 比率則是在這個特定日期生效的比率。

將相應的指數增長與每個金額相關聯是這個問題較為簡單的部分。可以通過使用下面的 JOIN 條件,將 Amounts 表與 IndexRates 表進行聯結:

ON IR.idx_date BETWEEN amt_date AND @givendate

對於每個金額您將擷取多個指數行,這些行代表著上述公式中的多個指數變化。使用左方外部聯結,可以確保生效日期晚於最後一個指數率更改日期的金額(例如,Amounts 中生效日期為 2002 年 12 月 24 日的行)也會顯示在輸出中。

在此處,更棘手的問題是根據公式的要求計算返回的所有指數變化的乘積。如果 T-SQL支援彙總 PRODUCT() 函數(該函數將計算一組值的乘積,這與 SUM() Function Compute一組值的和非常相似)的話,就可以使用 清單 2中顯示的偽查詢了。(不過,因為 T-SQL 不支援彙總 PRODUCT() 函數,所以您無法運行該查詢。)我在偽查詢的 SELECT 列表中使用了 ISNULL(),這樣當其生效日期晚於最後一個指數率變化日期時,也會返回本金金額。

在 2001 年 5 月發表的“Adding Performance”(InstantDoc ID 20131) 一文中,我討論了一個數學方法,此方法可通過在以下公式中使用 LOG10 函數來模仿彙總 PRODUCT() 函數:

PRODUCT() = POWER(10.,  SUM(LOG10()))

如果您將上面的公式應用於此問題的查詢,則會得到如下運算式:

ISNULL(POWER(CAST(10 AS FLOAT),  SUM(LOG10(1+idx_growth))),  amt_value) * amt_value

POWER() 函數的傳回值與其第一個參數的資料類型相同,因此該代碼會將數字 10 顯式轉換為 FLOAT,以強制輸出 FLOAT 結果。最後,將上面的運算式轉換為貨幣資料類型即可完成任務。您可以通過運行 清單 3顯示的最後一個查詢來擷取 Table 1 顯示的所需結果。

使用各自指數率係數的解決方案

解決該問題的另一種方法是使用 CPI 的不同表示形式。您可以儲存比率係數(該係數表示某個月的指數率與預定義的基月指數率的比值),而不是儲存指數率相對於上一個月的變化百分比。例如,可以將 2001 年 12 月確定為基月,並將它的比率係數值設定為 1。1 月的指數增長率為 0.1%,因此 1 月的指數率係數將為 1 * (1+0.1%) = 1.001。2 月的指數增長率為 0.2%,因此 2 月的指數率係數將為 1.001 * (1+0.2%),依此類推。請記住,每個指數率係數都包括自基礎日期以來所有的累積指數變化。因此,要向 amt_value 應用從 from_month 到 to_month 期間的指數變化,可以使用下面的公式:

amt_value * (to_index_rate / from_index_rate)

就這麼簡單。您可以儲存指數率係數(而非指數增長),也可以添加另一列,以便可以在所執行的各種財務計算中使用最適合您的需求的係數。甚至還可以在 INSERT 語句中僅指定一個值,並且讓觸發器為您計算另一個值。對您來說,一個重要的好處是,例如 IndexRates 這樣的表將沒有更新或刪除,而且對於每個期段(例如,每月),只需向該表中插入一行。

運行第 20 頁上 清單 4中的代碼,可以用另一列 idx_rate 重新建立 IndexRates 表。該代碼還會建立一個插入觸發器,該觸發器會基於儲存在 idx_growth 列中的指數增長值來計算指數率係數,並用指數變化重新填充該表。該觸發器運行一個 UPDATE 語句將 IndexRates 表和已插入表聯結起來,以便尋找需要進行更新的新行。SET 子句使用子查詢來檢索上個月的指數率係數,並將其與 (1 + current_month's_index_growth) 相乘。當在該表中輸入第一行時,整個運算式將產生 NULL。因此,在這種情況下,該代碼使用 ISNULL() 返回 1。

接著,您需要編寫一個查詢,以便對於每個金額尋找該金額的生效日期和特定日期的指數率係數;您將在本節前面提供的簡單公式中使用這些日期。在編寫該查詢時有一個問題,那就是 amt_date 和 @givendate 值不一定是當月的第 15 日。可以使用 CASE 運算式來檢查該日期的日期值是否小於 15,如果小於 15,則生效日期是上個月的第 15 日;否則,則是當月的第 15 日。因此,將按如下方式計算特定日期的生效日期:

DECLARE @givendate AS SMALLDATETIME SET @givendate = '20021231' DECLARE @effective_givendate AS SMALLDATETIME SET @effective_givendate =   CASE     WHEN DAY(@givendate) < 15       THEN CONVERT(CHAR(6), DATEADD(month, -1,        @givendate), 112)           + '15'     ELSE CONVERT(CHAR(6), @givendate, 112) + '15'   END

現在,可以使用類似的技術來計算該金額的生效日期,然後在 Amounts 表和 IndexRates 表的兩個執行個體之間使用一個三向聯結。一個執行個體檢索該金額生效日期的指數率係數;另一個執行個體檢索該特定日期的指數率係數。 清單 5顯示了這個完整的查詢。該代碼看上去比上一個解決方案中的代碼稍長,但是,它的執行速度會更快,因為它無需在每個金額的所需期段內尋找所有的指數變化,而只是尋找第一個和最後一個指數率係數。

學以致用

通常,用 T-SQL為財務問題提供解決方案看上去較為複雜。但是,當您擁有一個包含許多 T-SQL 技巧(如模仿彙總 PRODUCT() 函數)的工具箱時,就可以解決問題。嘗試為該問題提出幾個解決方案,在找到一個有效解決方案時,不要滿足。當您找到多個解決方案時,可以從中選擇一個此時最能滿足需求的解決方案,並使用已掌握的技術為下一個任務設計解決方案。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.