我正在运行一个包含多个连接的存储过程,其中一个表包含超过600,000条记录。问题是程序非常慢,可能需要几分钟才能执行。我们已将相关表格列编入索引但仍然没有运气。
我们可以做些什么来帮助提高绩效?查询发布在下面。
由于
with CTE
as
(
select * from
(
select distinct c.ContactId, c.FirstName, c.LastName,
(select top 1 ce.Email from dbo.ContactEmails as ce where ce.ContactId = c.ContactId and ce.IsPrimary = 1) as Email,
comp.CompanyName, j.JobName, c.MobileNumber, c.OfficeNumber, cse.DateSent, MAX(cse.DateSent) over(partition by ce.email) as maxdate
from dbo.ContactSentEmails as cse
join dbo.ContactEmails as ce on cse.ContactId = ce.ContactId
join dbo.Contacts as c on ce.ContactId = c.ContactId
left join dbo.Jobs as j on c.JobId = j.JobId
left join dbo.Companies as comp on c.CompanyId = comp.CompanyId
join dbo.StaffProjects as sp on cse.StaffProjectId = sp.StaffProjectId
join dbo.Staff as s on sp.StaffId = s.StaffId
join dbo.Projects as p on sp.ProjectId = p.ProjectId
where (@ContactSourceId = -1 or c.ContactSourceId = @ContactSourceId)
and (@FirstName = '' OR c.FirstName LIKE '%' + @FirstName + '%')
and (@LastName = '' OR c.LastName LIKE '%' + @LastName + '%')
and (@EmailAddress = '' OR ce.Email LIKE '%' + @EmailAddress + '%')
and (@StaffId = -1 or sp.StaffId = @StaffId)
and (@ProjectId = -1 or sp.ProjectId = @ProjectId)
and (@OfficeId = -1 or p.OfficeId = @OfficeId)
and cse.DateSent between CONVERT(datetime, @startDate) and CONVERT(datetime, @endDate)
group by c.ContactId, c.FirstName, c.LastName, Email,comp.CompanyName, j.JobName, c.MobileNumber, c.OfficeNumber, cse.DateSent
) as tbContacts
)
select ContactId, FirstName, LastName, Email, CompanyName, JobName, MobileNumber, OfficeNumber from CTE where cte.DateSent = CTE.maxdate order by CTE.Email
答案 0 :(得分:2)
正如@Joe在对你的问题的评论中所写的那样,LIKE
运算符很昂贵,特别是因为你将通配符放在每个搜索字符串的开头。众多OR
运算符也可能导致查询性能不佳。
请参阅Erland Sommarskog撰写的经典文章Dynamic Search Conditions in T-SQL,了解如何撰写效果更佳的“搜索”查询。
[此外,SELECT TOP 1
和group by ...
几乎与SELECT
子句中的每一列有什么关系?]
答案 1 :(得分:2)
许多缺陷!
缺陷1:如果不查看提供的值,则无法选择计划。
(@ContactSourceId = -1 or c.ContactSourceId = @ContactSourceId)
如果@ContactSourceId为-1,则您有一个查询执行计划。如果ContactSourceId不是-1,则您有另一个查询执行计划。如果你想要选择正确的执行计划,你需要提供一个查询,知道它应该被过滤的内容,而不需要查看变量的值。
由于您已使用此标准构造7次,因此有2 ^ 7个潜在计划,并且获得正确计划的几率为1/2 ^ 7 = 1/128 <1。 1%
您需要将此查询文本分解为128个不同的查询 - 查询优化器只会为您执行此操作。
缺陷2:未使用SARGable搜索条件
这128个查询中的一个是这样的:假设提供了@FirstName而其他变量没有提供。在这种情况下,@ FirstName是访问Contacts表的主要标准。
c.FirstName LIKE '%' + @FirstName + '%'
如果您在“联系人”表上仅使用此条件编写了查询 - 则无法将索引添加到将使用的“联系人”表中。你注定要进行表扫描。了解SARGable搜索条件。
缺陷3:每行的操作,每行产生相同的结果。
cse.DateSent between CONVERT(datetime, @startDate) and CONVERT(datetime, @endDate)
为什么要在每行上将变量转换为DateTime?在运行查询之前进行转换。
缺陷4:90%的时间Distinct是拐杖
distinct
top 1
group by
在查询中有这么多“只给我一个”运算符意味着查询作者只是尝试了东西并看到了什么。简化实际意图。我的猜测是不需要的。如果你在不需要的时候加上不同的东西 - 你仍然需要付费!
答案 2 :(得分:1)
正如@Joe和@Kenny所说,LIKE
运算符可能是导致问题的最主要原因。解决这个问题可能会带来最大的性能提升。调查full text search,看看它是否适合您的需求。
但是,我还会重构您选择电子邮件作为select子句中的子选择的方式。这通常是在SQL Server中执行查询的一种非常昂贵的方法。联系人可以在IsPrimary = 1
处有多个电子邮件地址吗?如果没有,那么只需加入主FROM
中的表格即可。如果他们可以有多个,请考虑编写一个视图(可能是索引视图)以通过联系方式返回顶部电子邮件地址。然后你可以加入。