SQL Server加速查询

时间:2011-10-20 14:06:52

标签: sql sql-server performance stored-procedures

我正在运行一个包含多个连接的存储过程,其中一个表包含超过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

3 个答案:

答案 0 :(得分:2)

正如@Joe在对你的问题的评论中所写的那样,LIKE运算符很昂贵,特别是因为你将通配符放在每个搜索字符串的开头。众多OR运算符也可能导致查询性能不佳。

请参阅Erland Sommarskog撰写的经典文章Dynamic Search Conditions in T-SQL,了解如何撰写效果更佳的“搜索”查询。

[此外,SELECT TOP 1group 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中的表格即可。如果他们可以有多个,请考虑编写一个视图(可能是索引视图)以通过联系方式返回顶部电子邮件地址。然后你可以加入。