存储过程
CREATE procedure [dbo].[ImproveProcedure] (@port varchar(50), @portdate datetime)
as
Declare @intdate datetime
select @intdate = max(rate_date) from Interestrate where rate_type = 'Zero'
and rate_date <= @portdate
Update transactiontable set NonDiscount = null, Discount = null, NonDiscountTcurr = null, DiscountTcurr = null,
NonDiscountNew = null, DiscountNew = null
where
port = @port and portdate = @portdate
Update tr set NonDiscount = (case sss_ind when 'P' then -exposure else exposure end) *
dbo.Foo(ccd, 'USD', @portdate,
case when datediff(m, portdate, isnull(priceend1, isnull(priceend2, mature))) < 1 then 1
when datediff(m, portdate, isnull(priceend1, isnull(priceend2, mature))) > 48 then 48
else datediff(m, portdate, isnull(priceend1, isnull(priceend2, mature)))
end),
NonDiscountTcurr = (case sss_ind when 'P' then -exposure else exposure end)
from
Phy p1
where
port = @port and portdate = @portdate
and
tr.trans = p1.trans
and
p1.Sub <> 'Option'
Update tr set NonDiscount = (case when buysell in ('A', 'S') then -vol * markprice else vol * markprice end) *
dbo.Foo(ccd, 'USD', @portdate,
case when datediff(m, portdate, isnull(priceend1, isnull(priceend2, mature))) < 1 then 1
when datediff(m, portdate, isnull(priceend1, isnull(priceend2, mature))) > 48 then 48
else datediff(m, portdate, isnull(priceend1, isnull(priceend2, mature)))
end),
NonDiscountTcurr = (case when buysell in ('A', 'S') then -vol * markprice else vol * markprice end)
from
Phy p1
where
port = @port and portfolio = @portfolio
and
tr.trans = p1.trans
and
p1.Sub = 'Option'
CREATE function [dbo].[Foo]
(@currency1 varchar(10),
@currency2 varchar(10),
@portdate datetime, @month int) returns float
As
BEGIN
Declare
@CurrentRate float,
@Ratedate datetime
select @Ratedate = max(rate_date) from fx where
(
currency1 = @currency1 and currency2 = @currency2 and forward_month = @month
or
currency2 = @currency1 and currency1 = @currency2 and forward_month = @month
)
and
@portdate >= Rate_date
IF exists ( select * from fx where currency1 = @currency1
and currency2 = @currency2 and rate_date = @Ratedate and forward_month = @month)
SELECT @CurrentRate = Rate from fx where currency1 = @currency1 and
currency2 = @currency2 and rate_date = @Ratedate and forward_month = @month
ELSE
IF exists ( select * from fx where currency1 =
@currency2 and currency2 = @currency1 and rate_date = @Ratedate and forward_month = @month)
select @CurrentRate = 1/Rate from fx where currency1 = @currency2
and currency2 = @currency1 and rate_date = @Ratedate and forward_month = @month
ELSE
select @CurrentRate = 1
return (@CurrRate )
END
我知道标量函数调用会限制查询的性能。有人可以看到一种方法来删除函数调用,用JOIN
orCROSS APPLY
或 something 代替它们,看看这是否有助于提高性能?我试过了,但没能找到办法。
有关当前性能的更多信息:单次运行该过程,更新约 25,000 行,运行需要 20-25 分钟。查看执行计划,我看到函数中有很多读取,我认为使用JOIN
解决方案(或类似解决方案),读取数量会急剧下降,性能可能会提高。另外,如上所述,我听说标量函数调用(通常)在查询中“不好”。
这是将函数重写为过程的尝试:
您的函数使用
ccd
、portdate
和之间的月数portdate
以及三个可能的日期(使用第一个非 NULL 的日期)来计算exposure
应该乘以的比率。currency1
是'USD',那么你乘以rate
;currency2
is 'USD',那么你除以rate
;rate
默认为 1因此,让我们尝试构建一个表格,其中包含您的可用费率。
我们基本上有两个因素来确定
rate_date
我们将使用的值:其中每一项的最长日期将决定所需的费率。
实际上,这最多让我们担心 48 *(货币数量)行。我猜那是 10-15000 行,而不是一个不合理的大表。
出于加入的目的,这将有助于货币标准化,所以第一个总是
USD
- 这样我们就不必担心我们是乘以汇率还是除以汇率。currency1
因此,我们构建了一个临时表,其中包含、currency2
和的唯一值forward_month
(始终将“USD”放在临时表的currency1
列中),以及rate_date
这三个值的每个唯一集合的最大值。然后,我们添加费率。如果相关,我们会像您一样做;如果我们遇到 'USD' 在
fx
as中的情况currency1
,我们会优先使用它而不是 'USD' 在 中的任何行currency2
。'USD'在的地方currrency1
,我们rate
直接入库;它在哪里currency2
,我们需要除以速率,所以我们存储1 / rate
。然后,我们不使用该函数来查找汇率,而是根据以下条件加入临时表:
currency1
是“美元”currency2
中ccd
的其他表rate_date
匹配的列portdate
(匹配传入的@portdate
)portdate
, 和 , 的第一个之间的月数相匹配priceend1
,priceend2
并且mature
这不是 NULL。(注意:COALESCE
获取两个或多个值的列表,并返回列表中的第一个非 NULL 值;它比 更容易阅读ISNULL(x,ISNULL(y,z))
,但工作原理相同)我们将其设为 a
LEFT JOIN
,因此我们包含在临时表中找不到匹配行的行。然后,我们只需将适当的值(取决于 的值
Sub
)乘以返回的速率(或乘以 1,如果我们返回 NULL,要么是因为临时表没有匹配项,要么是因为临时表有匹配行一个空rate
)。注意:我将您原来的两个 UPDATE 语句合并为一个,
CASE
在p1.Sub
. 为了保证它像您原来的两个语句版本一样工作,我还检查了是否p1.Sub
是NULL
;如果不能NULL
,那么您可以删除该支票。试试这个,看看它是否适合你。它没有完全优化(例如,我认为我们可以通过检查目标交易行中实际存在的货币来减少临时表中的行),但它确实消除了您正在使用的函数的逐行性质。
注意:代码未经测试。