我需要以某种方式优化我的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
答案 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.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)
请记住,这将加快此查询的速度,将占用数据库中的额外空间,并且在对表进行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。
希望有帮助。