Tsql 优化存储过程T-SQL
我需要以某种方式优化我的t-sql查询,但我在这方面没有太多经验。希望你的支持。如果我将许多RetailerID传递到where条件,那么它的执行将持续很长时间。显然,我必须首先重写子查询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
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.