优化多列LIKE SQL查询的最佳实践有哪些?

时间:2009-01-23 15:09:27

标签: sql search optimization sql-like

我有一个我正在继承并尝试优化的搜索查询。我很想知道是否有人有这方面的最佳做法和建议。生产服务器仍然是SQL Server 2000。

查询是一种高级客户搜索存储过程,它接受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

有没有人对如何提高查询效果有任何建议?

5 个答案:

答案 0 :(得分: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 + '%'

答案 1 :(得分: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
     )

它使查询更长,可能更复杂一些,但为了获得更好的性能,它可能是值得的。如果您的标准包含可能会被短路的子查询,则特别为真。

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

答案 2 :(得分:0)

  • 避免“或” - 它们通常会阻止使用索引
  • 切勿在左侧放置“%”。 - 同样的原因。

答案 3 :(得分:0)

我会尝试不让我的sql代码添加'%',而是希望参数已经拥有它,当然,这是在你的应用程序中验证它之后!然后不要包含'='比较,一直使用LIKE:

WHERE(cu.LastName LIKE @LastName)

而不是:

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

答案 4 :(得分:0)

您可以使用动态sql构建查询。这将消除大多数OR,也意味着您只需要在WHERE语句行中包含用户实际输入的参数。

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