Procedimento armazenado
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
Eu entendo que as chamadas de função escalar podem limitar o desempenho de uma consulta. Alguém pode ver uma maneira de remover as chamadas de função, substituindo-as por um JOIN
ou CROSS APPLY
ou algo, para ver se isso ajuda no desempenho? Eu tentei, mas não consegui encontrar uma maneira.
Mais informações sobre o desempenho atual: uma única execução do procedimento, atualizando cerca de 25.000 linhas, leva de 20 a 25 minutos para ser executada. Olhando para o plano de execução, vejo muitas leituras amarradas na função e estou pensando que com uma JOIN
solução (ou algo assim), esse número de leituras cairia drasticamente e o desempenho poderá melhorar. Além disso, como observado acima, ouvi dizer que as chamadas de função escalar são (em geral) "ruins" nas consultas.
Aqui está uma tentativa de reescrever a função no procedimento:
Sua função usa
ccd
,portdate
e o número de meses entreportdate
e três datas possíveis (usando a primeira que não é NULL) para calcular a taxa pela qualexposure
deve ser multiplicado.currency1
é 'USD', você multiplica porrate
;currency2
é 'USD', você divide porrate
;rate
padrão será 1Então, vamos tentar construir uma tabela com suas taxas disponíveis.
Temos basicamente dois fatores que identificam os
rate_date
valores que usaremos:A data máxima para cada um deles determinará qual taxa é necessária.
Isso nos dá no máximo 48 * (número de moedas) linhas para se preocupar, em termos práticos. Acho que são 10 a 15 mil linhas, não uma tabela excessivamente grande.
Para fins de junção, ajudará a padronizar a moeda, então a primeira é sempre
USD
- assim não precisamos nos preocupar se estamos multiplicando ou dividindo pela taxa.Portanto, construímos uma tabela temporária com os valores exclusivos de
currency1
,currency2
eforward_month
(sempre colocando 'USD' na coluna da tabela temporáriacurrency1
) e o máximorate_date
para cada conjunto exclusivo desses três valores.Em seguida, adicionamos as taxas. Caso seja relevante, faremos como você fez; Se tivermos um caso em que 'USD' esteja em
fx
ascurrency1
, usaremos isso de preferência em qualquer linha em que 'USD' esteja emcurrency2
. Onde 'USD' está emcurrrency1
, armazenamosrate
diretamente; onde estácurrency2
, precisamos dividir pela taxa, então armazenamos1 / rate
.Então, em vez de usar a função para encontrar a taxa, juntamos à tabela temporária, com base em:
currency1
sendo 'USD'currency2
sendo accd
das outras tabelas na consultarate_date
correspondência daportdate
coluna (que corresponde à passada@portdate
)portdate
, e o primeiro depriceend1
,priceend2
emature
isso não é NULL. (Nota:COALESCE
pega uma lista de dois ou mais valores e retorna o primeiro valor não NULL da lista; é um pouco mais fácil de ler do queISNULL(x,ISNULL(y,z))
, mas funciona da mesma forma)Tornamos isso um
LEFT JOIN
, então incluímos linhas onde não podemos encontrar uma linha correspondente na tabela temporária.Em seguida, simplesmente multiplicamos o valor apropriado (dependendo do valor de
Sub
) pela taxa retornada (ou por 1, se retornarmos um NULL, seja porque a tabela temporária não correspondia ou porque a tabela temporária tinha uma linha correspondente com um NULOrate
).NOTA: Combinei suas duas instruções UPDATE originais em uma, adicionando outra
CASE
no valor dep1.Sub
. Para garantir que isso esteja funcionando como sua versão original de duas instruções, também coloquei uma verificação para ver sep1.Sub
éNULL
; se isso não puder serNULL
, você poderá remover essa verificação.Experimente isso e veja se funciona para você. Não é totalmente otimizado (acho que poderíamos reduzir as linhas na tabela temporária verificando as moedas que estão realmente em nossas linhas de transação de destino, por exemplo), mas remove a natureza linha por linha da função que você estava usando.
NOTA: o código não foi testado.