Tsql 优化存储过程T-SQL

Tsql 优化存储过程T-SQL,tsql,stored-procedures,query-optimization,Tsql,Stored Procedures,Query Optimization,我需要以某种方式优化我的t-sql查询,但我在这方面没有太多经验。希望你的支持。如果我将许多RetailerID传递到where条件,那么它的执行将持续很长时间。显然,我必须首先重写子查询 Select LeadRetailerId, Count(*) as NumberGreenLeads from [MBCH_LMT].[lead].[Contact] a inner join [MBCH_LMT].[lead].[ContactActivity] b on a.UID = b.Conta

我需要以某种方式优化我的t-sql查询,但我在这方面没有太多经验。希望你的支持。如果我将许多RetailerID传递到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

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

这些查询中的每一个都可以转换为索引视图,只需稍加清理(例如,
COUNT
将需要变为
COUNT\u BIG

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

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

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

如果这些表处于高事务性环境中,则索引视图可能不是一个选项;一定要测试,测试。显然需要维护dbo.LastYear表。

我对第一个查询做了一些修改(为了可读性)

尽管事情的顺序已经改变,MSSQL(很可能)将执行相同的查询计划。我注意到的第一件事是(@RetailerId)查询中有
a.leaderretailerid。你打算增加额外的价值还是仅仅是这个?在一个变量中包含多个值不会像您当时所想的那样起作用,一些简单的实验应该会很快证明这一点。
如果只使用1值,则可以将
中的
替换为
=
,并且可以将查询简化为

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.ContactUID
b.LeadStatusDate

CREATE INDEX idx_test ON [MBCH_LMT].[lead].[Contact] (LeadRetailerId) INCLUDE (UID)
CREATE INDEX idx_test ON [MBCH_LMT].[lead].[ContactActivity] (ContactUID, LeadStatusDate)
请记住,这将加快此查询的速度,这将占用数据库中的额外空间,并且在表上插入/更新/删除时会产生一些开销。(没有免费的午餐之类的东西,尽管我现在不会太担心,但索引的好处往往大于开销……如果合理使用=)

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

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

使用SQL需要记住的一点是,系统将解释您的查询,并提出它认为最优化的执行计划。这个计划不仅取决于您的查询,还取决于索引的可用性、表中数据的数量和类型,有时还取决于一些黑魔法=)

我认为您是存储过程中参数嗅探的受害者。 信不信由你,这是一件大事(有一天读一下),但可能很容易解决

如果查询中有@RetailerId,则只需在查询中设置一个本地参数

declare @_RetailerId  bigint
set @_RetailerId = @RetailerId  -- the one you pass from your stored proc parameters.
你可能会感到惊讶。查询计划现在完全不同了。

没什么想法

1) 您也可以编写
Count(1)
,而不是
Count(*)
——不过可能不会有太大的区别

2) 您可以将第二个
内部联接
替换为
存在的位置
。Exists在某些情况下比Join更快

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

4) 您还可以尝试创建#表,以便为大量记录编制索引

5)如果行不是大的或大的,考虑用CTE(通用表表达式)重新编写查询,这样你就不需要做第4步了。


希望有帮助。

对于初学者,您可以在两个位置删除(1=1),尽管SSM可能会在幕后执行此操作。…
取决于@RetailerId中的值数。
??根据定义,其中只能有1个值。当然,它只能接受一个字符串值,但鉴于(@RetailerId)
语法中的
,该字符串似乎用于保存多个值。这确实令人困惑。。。关于用户真正想要什么、他要求什么以及最终得到什么的反复出现的难题=)
declare @_RetailerId  bigint
set @_RetailerId = @RetailerId  -- the one you pass from your stored proc parameters.