2009-01-23 73 views
2

我有一个搜索查询,我继承并试图优化。我很好奇听到有没有人有最佳做法和建议。生产服务器仍然是SQL Server 2000。优化多列LIKE SQL查询的一些最佳实践是什么?

该查询是一个高级客户搜索存储过程,它接受5个不同的搜索条件参数(即名字,姓氏,地址,电话等)来搜索数百万条记录表。 WHERE子句中所有连接的列和列都有索引。另外,最初的查询将记录转储为一个用于寻呼容量的表变量。

INSERT INTO @tempCustTable (CustomerID, FirstName, LastName, City, StateProvince, Zip, PhoneNumber) 
SELECT DISTINCT cu.CustomerID, cu.FirstName, cu.LastName, a.City, 
a.StateProvince, a.Zip, p.PhoneNumber 
FROM Customer cu WITH(NOLOCK) 
LEFT OUTER JOIN Address a WITH(NOLOCK) ON cu.CustomerID = a.CustomerID 
LEFT OUTER JOIN Phone p WITH(NOLOCK) ON cu.CustomerID = p.CustomerID 
WHERE (cu.LastName = @LastName OR cu.LastName LIKE @LastName + '%') 
AND (@FirstName IS NULL OR cu.FirstName = @FirstName OR cu.FirstName LIKE @FirstName + '%') 
AND (@StateProvince = '' OR a.StateProvince LIKE @StateProvince) 
AND (@City = '' OR a.City LIKE @City + '%') 
AND (@Zip = '' OR a.Zip = @Zip OR a.Zip LIKE @Zip + '%') 
ORDER BY cu.LastName, cu.FirstName 

有没有人有任何关于如何提高查询性能的建议?

回答

2

这不就是整条生产线

AND (@Zip = '' OR a.Zip = @Zip OR a.Zip LIKE @Zip + '%') 

与此相同

AND (a.Zip LIKE @Zip + '%') 

肯定

AND (a.Zip LIKE @Zip + '%') 

它是一样的

a.Zip = @Zip OR a.Zip LIKE @Zip + '%' 
+0

好问题。我需要再次查询SQL OR来查看它是否评估双方,而不管左侧的返回。 – JamesEggers 2009-01-23 15:21:39

0
  • 避免“或” S - 他们一般避免使用索引
  • 不要把一个“%”左侧。 - 同样的原因。
+0

是的,它可能会限制你的搜索,但是如果这是一个问题,如果这是一个问题,那么升级你的服务器,或者至少将I/O升级到光纤存储。 – TravisO 2009-01-23 15:15:22

+0

根据我提供的标准组合来执行大量的IF语句来构建查询会更好吗?看起来像过度杀毒和大量的代码复制和粘贴,但我是游戏尝试不同的事情,使这个更好。 – JamesEggers 2009-01-23 15:20:51

0

我会尽量不让我的sql代码添加'%',而是期望参数已经拥有它,当然,在您的应用程序中验证它之后,这个参数就已经拥有了!然后,不包括 '=' 比较,使用像所有的时间:

WHERE(LIKE cu.LastName @LastName)的

代替:

WHERE(cu.LastName = @LastName或Cu .LastName LIKE @LastName +'%')

0

您可以使用动态sql建立查询。这将摆脱你的大部分ORs,也意味着你只需要在WHERE语句行中包含用户确实输入的参数。

如果您这样做,一定要使用sp_executesql而不是exec,以便您可以参数化动态sql,以便可以缓存查询计划。

1

SQLMenace指出,你可以清除代码中的大量冗余。

另一件事是,ORDER BY不应该与INSERT..SELECT一起使用。在这种情况下,ORDER BY是没有意义的。人们偶尔使用它来强制IDENTITY列以某种方式行事,但这是一个坏习惯IMO。

我不知道这是否有助于您的情况,但最近我遇到的一件事是,在存储过程中SQL Server(我使用2005年,但也可能是2000年)也不会短在许多情况下电路OR条件。例如,当您使用:

@my_parameter IS NULL OR my_column = @my_parameter 

即使您为@my_parameter传入NULL值,它仍然会评估下半部分。即使我将存储过程设置为重新编译(和SELECT),也会发生这种情况。诀窍是通过使用CASE语句强制短路。使用该伎俩(除去一些冗余)你的声明是这样的:

INSERT INTO @tempCustTable 
(
    CustomerID, 
    FirstName, 
    LastName, 
    City, 
    StateProvince, 
    Zip, 
    PhoneNumber 
) 
SELECT DISTINCT 
    cu.CustomerID, 
    cu.FirstName, 
    cu.LastName, 
    a.City, 
    a.StateProvince, 
    a.Zip, 
    p.PhoneNumber 
FROM Customer cu WITH(NOLOCK) 
LEFT OUTER JOIN Address a WITH(NOLOCK) ON cu.CustomerID = a.CustomerID 
LEFT OUTER JOIN Phone p WITH(NOLOCK) ON cu.CustomerID = p.CustomerID 
WHERE 
    (cu.LastName LIKE @LastName + '%') AND 
    (1 = 
      CASE 
       WHEN @FirstName IS NULL THEN 1 
       WHEN cu.FirstName LIKE @FirstName + '%' THEN 1 
       ELSE 0 
      END 
    ) AND 
    (1 = 
      CASE 
       WHEN @StateProvince = '' THEN 1 
       WHEN a.StateProvince = @StateProvince THEN 1 
       ELSE 0 
      END 
    ) AND 
    (1 = CASE 
       WHEN @City = '' THEN 1 
       WHEN a.City LIKE @City + '%' THEN 1 
       ELSE 0 
      END 
    ) AND 
    (1 = CASE 
       WHEN @Zip = '' THEN 1 
       WHEN a.Zip LIKE @Zip + '%' THEN 1 
       ELSE 0 
      END 
    ) 

它使查询更长,也许稍微复杂一些,但它可能是值得的,更好的性能。这是,特别是如果您的条件包含可能会被短路的子查询,则为true。

最后... 与您的参数保持一致。对于@FirstName,你检查一个NULL值以确定它是否被使用,但是对于你检查空字符串的其他人。基本编码101在这里,你需要小心。