AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • 主页
  • 系统&网络
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • 主页
  • 系统&网络
    • 最新
    • 热门
    • 标签
  • Ubuntu
    • 最新
    • 热门
    • 标签
  • Unix
    • 最新
    • 标签
  • DBA
    • 最新
    • 标签
  • Computer
    • 最新
    • 标签
  • Coding
    • 最新
    • 标签
主页 / dba / 问题 / 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

如何提高存储过程的性能?

  • 772

存储过程

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  

我知道标量函数调用会限制查询的性能。有人可以看到一种方法来删除函数调用,用JOINorCROSS APPLY或 something 代替它们,看看这是否有助于提高性能?我试过了,但没能找到办法。

有关当前性能的更多信息:单次运行该过程,更新约 25,000 行,运行需要 20-25 分钟。查看执行计划,我看到函数中有很多读取,我认为使用JOIN解决方案(或类似解决方案),读取数量会急剧下降,性能可能会提高。另外,如上所述,我听说标量函数调用(通常)在查询中“不好”。

sql-server performance
  • 1 1 个回答
  • 748 Views

1 个回答

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

    这是将函数重写为过程的尝试:

    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;
    

    您的函数使用ccd、portdate和之间的月数portdate以及三个可能的日期(使用第一个非 NULL 的日期)来计算exposure应该乘以的比率。

    • 如果你能找到一个匹配,其中currency1是'USD',那么你乘以rate;
    • 如果唯一的匹配是 where currency2is 'USD',那么你除以rate;
    • 如果未找到匹配项,则rate默认为 1

    因此,让我们尝试构建一个表格,其中包含您的可用费率。

    我们基本上有两个因素来确定rate_date我们将使用的值:

    • 月份,1 到 48 之间的整数;
    • 两种货币,其中一种是“美元”,另一种会有所不同。

    其中每一项的最长日期将决定所需的费率。

    实际上,这最多让我们担心 48 *(货币数量)行。我猜那是 10-15000 行,而不是一个不合理的大表。

    出于加入的目的,这将有助于货币标准化,所以第一个总是USD- 这样我们就不必担心我们是乘以汇率还是除以汇率。

    currency1因此,我们构建了一个临时表,其中包含、currency2和的唯一值forward_month(始终将“USD”放在临时表的currency1列中),以及rate_date这三个值的每个唯一集合的最大值。

    然后,我们添加费率。如果相关,我们会像您一样做;如果我们遇到 'USD' 在fxas中的情况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,那么您可以删除该支票。

    试试这个,看看它是否适合你。它没有完全优化(例如,我认为我们可以通过检查目标交易行中实际存在的货币来减少临时表中的行),但它确实消除了您正在使用的函数的逐行性质。

    注意:代码未经测试。

    • 1

相关问题

  • 死锁的主要原因是什么,可以预防吗?

  • 如何确定是否需要或需要索引

  • 我在哪里可以找到mysql慢日志?

  • 如何优化大型数据库的 mysqldump?

Sidebar

Stats

  • 问题 205573
  • 回答 270741
  • 最佳答案 135370
  • 用户 68524
  • 热门
  • 回答
  • Marko Smith

    连接到 PostgreSQL 服务器:致命:主机没有 pg_hba.conf 条目

    • 12 个回答
  • Marko Smith

    如何让sqlplus的输出出现在一行中?

    • 3 个回答
  • Marko Smith

    选择具有最大日期或最晚日期的日期

    • 3 个回答
  • Marko Smith

    如何列出 PostgreSQL 中的所有模式?

    • 4 个回答
  • Marko Smith

    列出指定表的所有列

    • 5 个回答
  • Marko Smith

    如何在不修改我自己的 tnsnames.ora 的情况下使用 sqlplus 连接到位于另一台主机上的 Oracle 数据库

    • 4 个回答
  • Marko Smith

    你如何mysqldump特定的表?

    • 4 个回答
  • Marko Smith

    使用 psql 列出数据库权限

    • 10 个回答
  • Marko Smith

    如何从 PostgreSQL 中的选择查询中将值插入表中?

    • 4 个回答
  • Marko Smith

    如何使用 psql 列出所有数据库和表?

    • 7 个回答
  • Martin Hope
    Jin 连接到 PostgreSQL 服务器:致命:主机没有 pg_hba.conf 条目 2014-12-02 02:54:58 +0800 CST
  • Martin Hope
    Stéphane 如何列出 PostgreSQL 中的所有模式? 2013-04-16 11:19:16 +0800 CST
  • Martin Hope
    Mike Walsh 为什么事务日志不断增长或空间不足? 2012-12-05 18:11:22 +0800 CST
  • Martin Hope
    Stephane Rolland 列出指定表的所有列 2012-08-14 04:44:44 +0800 CST
  • Martin Hope
    haxney MySQL 能否合理地对数十亿行执行查询? 2012-07-03 11:36:13 +0800 CST
  • Martin Hope
    qazwsx 如何监控大型 .sql 文件的导入进度? 2012-05-03 08:54:41 +0800 CST
  • Martin Hope
    markdorison 你如何mysqldump特定的表? 2011-12-17 12:39:37 +0800 CST
  • Martin Hope
    Jonas 如何使用 psql 对 SQL 查询进行计时? 2011-06-04 02:22:54 +0800 CST
  • Martin Hope
    Jonas 如何从 PostgreSQL 中的选择查询中将值插入表中? 2011-05-28 00:33:05 +0800 CST
  • Martin Hope
    Jonas 如何使用 psql 列出所有数据库和表? 2011-02-18 00:45:49 +0800 CST

热门标签

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

Explore

  • 主页
  • 问题
    • 最新
    • 热门
  • 标签
  • 帮助

Footer

AskOverflow.Dev

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve