优化存储过程T-SQL

时间:2019-06-07 14:17:26

标签: tsql stored-procedures query-optimization

我需要以某种方式优化我的t-sql查询,但是我没有很多经验。希望得到您的支持。如果我将许多RetailersID传递到where条件,则该过程的执行将持续很长时间。显然我必须在第一个

中重写子查询
Select LeadRetailerId, Count(*) as NumberGreenLeads
from  [MBCH_LMT].[lead].[Contact] a
inner join [MBCH_LMT].[lead].[ContactActivity] b
on a.UID = b.ContactUID
where 1=1
    AND b.LeadStatusDate > DATEADD(Year, -1, GetDate())
    AND a.LeadRetailerId in (@RetailerId)
    group by a.LeadRetailerId


Select Table_1.LeadRetailerId, Table_1.EscalationLevel ,Count(Table_1.EscalationLevel) as Number
from (
    select a.LeadRetailerId, max(EscalationLevel) as EscalationLevel
    from [MBCH_LMT].[lead].[Contact]  a
    inner join [MBCH_LMT].[lead].[ContactActivityEscalationHistory] b
    on b.ContactUID = a.UID
    where 1=1
    AND a.LeadRetailerId in (@RetailerId) 
    group by ContactUID, LeadRetailerId) as Table_1
Group by EscalationLevel, LeadRetailerId

5 个答案:

答案 0 :(得分:1)

我建议采用值DATEADD(Year,-1,GETDATE())并将其放在变量中,这将简化连接的那一部分。此外,取决于@RetailerId中值的数量,可以将其放入表变量或类似变量中以帮助其在查询中使用。然后,我将显示实际的执行计划,并查看是否建议缺少任何索引。

答案 1 :(得分:0)

可以将这些查询中的每一个都转换为带有少量清理的索引视图(例如,COUNT必须成为COUNT_BIG。)

但是,对于第一个查询,您不能具有GETDATE()表达式并为视图建立索引。为此,您必须创建一个名为dbo.LastYear之类的表,其中包含上一年的一行。我们称之为 Yr 。然后可以替换:

AND b.LeadStatusDate > DATEADD(Year, -1, GetDate())

使用:

JOIN dbo.LastYear AS ly ON b.LeadStatusDate = ly.Yr

如果这些表处于高事务环境中,则可能无法选择索引视图;确保测试,测试。显然需要维护dbo.LastYear表。

答案 2 :(得分:0)

我已经对第一个查询做了一些修改(以提高可读性)

SELECT LeadRetailerId, Count(*) as NumberGreenLeads
  FROM  [MBCH_LMT].[lead].[Contact] a
 INNER JOIN [MBCH_LMT].[lead].[ContactActivity] b
         ON b.ContactUID = a.UID
        AND b.LeadStatusDate > DATEADD(Year, -1, GetDate())
 WHERE a.LeadRetailerId in (@RetailerId)
 GROUP BY a.LeadRetailerId

尽管事物的顺序已更改,但MSSQL将(最有可能)执行相同的查询计划。我注意到的第一件事是查询中有a.LeadRetailerId in (@RetailerId)。您打算增加附加值还是仅此一项?一个变量中包含多个值将无法正常工作,您可能会很快想到一些简单的实验。 如果仅使用1值,则可以将IN替换为=,并将查询简化为

SELECT LeadRetailerId = @RetailerId, 
       Count(*) as NumberGreenLeads
  FROM [MBCH_LMT].[lead].[Contact] a
 INNER JOIN [MBCH_LMT].[lead].[ContactActivity] b
         ON b.ContactUID = a.UID
        AND b.LeadStatusDate > DATEADD(Year, -1, GetDate())
 WHERE a.LeadRetailerId = @RetailerId

这可能会快一点。

按照您的描述,但是我认为您正在使用这种方式:

SELECT LeadRetailerId, Count(*) as NumberGreenLeads
  FROM  [MBCH_LMT].[lead].[Contact] a
 INNER JOIN [MBCH_LMT].[lead].[ContactActivity] b
         ON b.ContactUID = a.UID
        AND b.LeadStatusDate > DATEADD(Year, -1, GetDate())
 WHERE a.LeadRetailerId in (@RetailerId1, @RetailerId2, @RetailerId3, @RetailerId4, @RetailerId5, etc)
 GROUP BY a.LeadRetailerId

列表按需增长的位置。 如果这种情况很快变慢,则可以检查表上是否有任何索引可以帮助MSSQL找到快速的处理方法。理想情况下,您将在a.LeadRetailerId上有一个索引,在第一个表上包含a.UID,在b.ContactUIDb.LeadStatusDate

上在另一个表上包含另一个索引
CREATE INDEX idx_test ON [MBCH_LMT].[lead].[Contact] (LeadRetailerId) INCLUDE (UID)
CREATE INDEX idx_test ON [MBCH_LMT].[lead].[ContactActivity] (ContactUID, LeadStatusDate)

请记住,这将加快此查询的速度,将占用数据库中的额外空间,并且在对表进行INSERT/UPDATE/DELETE-ing时会产生一些开销。 (没有免费的午餐之类的东西,尽管我现在不担心它,但是索引的好处通常会超过开销……当明智地使用时=)

第二个查询有点奇怪,因为您执行了GROUP BY b.ContactUID。我不熟悉您的表结构,但是您确定所说查询的结果是您想要的吗?无论如何,如果有的话,它应该受益于第一张表上相同的建议索引。对于第三张表,我建议:

CREATE INDEX idx_test ON [MBCH_LMT].[lead].[ContactActivityEscalationHistory] (ContactUID, EscalationLevel)

SQL要记住的一件事是,系统将解释您的查询并提出它认为是最佳的执行计划。该计划不仅取决于您的查询,还取决于索引的可用性,表中数据的数量和类型,有时还包括一些黑魔法=)

答案 3 :(得分:0)

我认为您是存储过程中参数嗅探的受害者。 信不信由你,这是一笔不小的数目(有一天会读到它),但是可能很容易解决。

如果查询中有@RetailerId,则只需在查询中输入本地参数即可。

declare @_RetailerId  bigint
set @_RetailerId = @RetailerId  -- the one you pass from your stored proc parameters.

您可能会感到惊讶。现在,查询计划完全不同。

答案 4 :(得分:0)

很少有想法,

1)您也可以写Count(*)而不是Count(1)-可能没什么大作用

2)您可以将第二个Inner Join替换为Where Exists。与Join相比,在某些情况下,存在速度更快

3)正如正确提到的那样,参数嗅探有很大的不同。

4)您还可以尝试创建#Table,在其中可以为大量记录建立索引。

5)如果行不大也不庞大,请考虑使用CTE(公用表表达式)重写查询,这样就无需执行步骤4。

希望有帮助。