AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • Início
  • system&network
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • Início
  • system&network
    • Recentes
    • Highest score
    • tags
  • Ubuntu
    • Recentes
    • Highest score
    • tags
  • Unix
    • Recentes
    • tags
  • DBA
    • Recentes
    • tags
  • Computer
    • Recentes
    • tags
  • Coding
    • Recentes
    • tags
Início / dba / Perguntas / 188115
Accepted
V_immo
V_immo
Asked: 2017-10-11 07:13:38 +0800 CST2017-10-11 07:13:38 +0800 CST 2017-10-11 07:13:38 +0800 CST

Como posso melhorar o desempenho do procedimento armazenado?

  • 772

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 JOINou CROSS APPLYou 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 JOINsoluçã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.

sql-server performance
  • 1 1 respostas
  • 748 Views

1 respostas

  • Voted
  1. Best Answer
    RDFozz
    2017-10-11T12:29:23+08:002017-10-11T12:29:23+08:00

    Aqui está uma tentativa de reescrever a função no procedimento:

    CREATE PROCEDURE [dbo].[ImproveProcedure]
    (
      @port varchar(50)
     ,@portdate datetime
    )
    AS   
    BEGIN
        DECLARE @intdate datetime;
        SELECT @intdate = MAX(rate_date)
          FROM Interestrate
         WHERE rate_type = 'Zero'
           AND rate_date <= @portdate
        ;
    
        IF (OBJECT_ID('tempdb..#tmp_fx') IS NOT NULL) DROP TABLE #tmp_fx;
        CREATE TABLE #tmp_fx
             ( currency1 varchar(10)
              ,currency2 varchar(10)
              ,month int
              ,rate_date datetime
              ,rate float
              ,CONSTRAINT PK_tmp_fx PRIMARY KEY (currency2, month)
             );
    
        -- NOTE: currency1 not needed in PK, as it's always 'USD' - may want to change if add'l values are added.
    
        -- Get base currency pairs, months, and max dates
        INSERT INTO #tmp_fx
        SELECT currency1, currency2, forward_month, MAX(rate_date)
          FROM (SELECT currency1, currency2, forward_month, rate_date
                  FROM fx
                 WHERE currency1 = 'USD'
                   AND rate_date <= @portdate
                UNION ALL
                SELECT currency2, currency1, forward_month, rate_date
                  FROM fx
                 WHERE currency2 = 'USD'
                   AND rate_date <= @portdate
               ) sq
         GROUP BY currency1, currency2, forward_month
        ;
    
        -- Set rates based on previous query:
        --   Step 1: rates where currency1 = currency1 -- multiply
        UPDATE t
           SET rate = fx.Rate
          FROM #tmp_fx t
                 INNER JOIN fx ON (    t.currency1 = fx.currency1
                                   AND t.currency2 = fx.currency2
                                   AND t.month = fx.forward_month
                                   AND t.rate_date = fx.rate_date
                                  )
        ;
    
        --   Step 2: rates where currency2 = currency1 -- divide
        UPDATE t
           SET rate = 1 / fx.Rate
          FROM #tmp_fx t
                 INNER JOIN fx ON (    t.currency2 = fx.currency1
                                   AND t.currency1 = fx.currency2
                                   AND t.month = fx.forward_month
                                   AND t.rate_date = fx.rate_date
                                  )
         WHERE fx.rate IS NULL
        ;
    
        -- NOTE - unset rates left NULL - will force to 1 in final query
    
    
        UPDATE tr
           SET NonDiscount = null
              ,Discount = null
              ,NonDiscountTcurr = null
              ,DiscountTcurr = null
              ,NonDiscountNew = null
              ,DiscountNew = null    
         WHERE port = @port
           AND portdate = @portdate
        ;
    
        UPDATE tr
           SET NonDiscount =  CASE WHEN p1.Sub <> 'Option'
                                THEN CASE sss_ind
                                       WHEN 'P' then -exposure
                                       ELSE          exposure
                                     END
                                ELSE CASE WHEN buysell IN ('A', 'S')
                                       THEN -vol * markprice
                                       ELSE vol * markprice
                                     END
                              END
                            * ISNULL(fx.Rate, CAST(1 as float))
              ,NonDiscountTcurr = CASE WHEN p1.Sub <> 'Option'
                                    THEN CASE sss_ind
                                           WHEN 'P' then -exposure
                                           ELSE          exposure
                                         END
                                    ELSE CASE WHEN buysell IN ('A', 'S')
                                           THEN -vol * markprice
                                           ELSE vol * markprice
                                         END
                                  END
          FROM tr
                 INNER JOIN Phy p1 ON (tr.trans = p1.trans)
                 LEFT  JOIN #tmp_fx fx ON (    'USD' = fx.currency1
                                           AND ccd = fx.currency2
                                           AND portdate = fx.rate_date
                                           AND  CASE 
                                                  WHEN DATEDIFF(month, portdate, COALESCE(priceend1, priceend2, mature)) < 1
                                                    THEN 1
                                                  WHEN DATEDIFF(month, portdate, COALESCE(priceend1, priceend2, mature)) > 48
                                                    THEN 48
                                                  ELSE   DATEDIFF(month, portdate, COALESCE(priceend1, priceend2, mature))
                                                END
                                              = fx.month
                                          )
         WHERE port = @port
           AND portdate = @portdate
           AND p1.Sub IS NOT NULL
        ;
    END;
    

    Sua função usa ccd, portdatee o número de meses entre portdatee três datas possíveis (usando a primeira que não é NULL) para calcular a taxa pela qual exposuredeve ser multiplicado.

    • Se você encontrar uma correspondência onde currency1é 'USD', você multiplica por rate;
    • Se a única correspondência for onde currency2é 'USD', você divide por rate;
    • se nenhuma correspondência for encontrada, o ratepadrão será 1

    Então, vamos tentar construir uma tabela com suas taxas disponíveis.

    Temos basicamente dois fatores que identificam os rate_datevalores que usaremos:

    • o mês, um número inteiro entre 1 e 48;
    • as duas moedas, uma das quais será 'USD', a outra irá variar.

    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, currency2e forward_month(sempre colocando 'USD' na coluna da tabela temporária currency1) e o máximo rate_datepara 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 fxas currency1, usaremos isso de preferência em qualquer linha em que 'USD' esteja em currency2. Onde 'USD' está em currrency1, armazenamos ratediretamente; onde está currency2, precisamos dividir pela taxa, então armazenamos 1 / rate.

    Então, em vez de usar a função para encontrar a taxa, juntamos à tabela temporária, com base em:

    • a tabela temporária está currency1sendo 'USD'
    • a tabela temporária está currency2sendo a ccddas outras tabelas na consulta
    • a rate_datecorrespondência da portdatecoluna (que corresponde à passada @portdate)
    • o mês corresponde ao número de meses entre portdate, e o primeiro de priceend1, priceend2e matureisso não é NULL. (Nota: COALESCEpega 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 que ISNULL(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 NULO rate).

    NOTA: Combinei suas duas instruções UPDATE originais em uma, adicionando outra CASEno valor de p1.Sub. Para garantir que isso esteja funcionando como sua versão original de duas instruções, também coloquei uma verificação para ver se p1.Subé NULL; se isso não puder ser NULL, 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.

    • 1

relate perguntas

  • Quais são as principais causas de deadlocks e podem ser evitadas?

  • Como determinar se um Índice é necessário ou necessário

  • Onde posso encontrar o log lento do mysql?

  • Como posso otimizar um mysqldump de um banco de dados grande?

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    conectar ao servidor PostgreSQL: FATAL: nenhuma entrada pg_hba.conf para o host

    • 12 respostas
  • Marko Smith

    Como fazer a saída do sqlplus aparecer em uma linha?

    • 3 respostas
  • Marko Smith

    Selecione qual tem data máxima ou data mais recente

    • 3 respostas
  • Marko Smith

    Como faço para listar todos os esquemas no PostgreSQL?

    • 4 respostas
  • Marko Smith

    Listar todas as colunas de uma tabela especificada

    • 5 respostas
  • Marko Smith

    Como usar o sqlplus para se conectar a um banco de dados Oracle localizado em outro host sem modificar meu próprio tnsnames.ora

    • 4 respostas
  • Marko Smith

    Como você mysqldump tabela (s) específica (s)?

    • 4 respostas
  • Marko Smith

    Listar os privilégios do banco de dados usando o psql

    • 10 respostas
  • Marko Smith

    Como inserir valores em uma tabela de uma consulta de seleção no PostgreSQL?

    • 4 respostas
  • Marko Smith

    Como faço para listar todos os bancos de dados e tabelas usando o psql?

    • 7 respostas
  • Martin Hope
    Jin conectar ao servidor PostgreSQL: FATAL: nenhuma entrada pg_hba.conf para o host 2014-12-02 02:54:58 +0800 CST
  • Martin Hope
    Stéphane Como faço para listar todos os esquemas no PostgreSQL? 2013-04-16 11:19:16 +0800 CST
  • Martin Hope
    Mike Walsh Por que o log de transações continua crescendo ou fica sem espaço? 2012-12-05 18:11:22 +0800 CST
  • Martin Hope
    Stephane Rolland Listar todas as colunas de uma tabela especificada 2012-08-14 04:44:44 +0800 CST
  • Martin Hope
    haxney O MySQL pode realizar consultas razoavelmente em bilhões de linhas? 2012-07-03 11:36:13 +0800 CST
  • Martin Hope
    qazwsx Como posso monitorar o andamento de uma importação de um arquivo .sql grande? 2012-05-03 08:54:41 +0800 CST
  • Martin Hope
    markdorison Como você mysqldump tabela (s) específica (s)? 2011-12-17 12:39:37 +0800 CST
  • Martin Hope
    Jonas Como posso cronometrar consultas SQL usando psql? 2011-06-04 02:22:54 +0800 CST
  • Martin Hope
    Jonas Como inserir valores em uma tabela de uma consulta de seleção no PostgreSQL? 2011-05-28 00:33:05 +0800 CST
  • Martin Hope
    Jonas Como faço para listar todos os bancos de dados e tabelas usando o psql? 2011-02-18 00:45:49 +0800 CST

Hot tag

sql-server mysql postgresql sql-server-2014 sql-server-2016 oracle sql-server-2008 database-design query-performance sql-server-2017

Explore

  • Início
  • Perguntas
    • Recentes
    • Highest score
  • tag
  • help

Footer

AskOverflow.Dev

About Us

  • About Us
  • Contact Us

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve