2

我有一个正在继承并尝试优化的搜索查询。我很想知道是否有人对此有任何最佳实践和建议。生产服务器仍然是 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

有人对如何提高查询性能有任何建议吗?

4

5 回答 5

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 + '%'
于 2009-01-23T15:15:46.803 回答
2

正如 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。

于 2009-01-23T18:37:48.033 回答
1
  • 避免使用“OR”——它们通常会阻止使用索引
  • 切勿在左侧放置“%”。- 同样的原因。
于 2009-01-23T15:10:41.540 回答
0

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

WHERE (cu.LastName LIKE @LastName)

代替:

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

于 2009-01-23T15:20:16.263 回答
0

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

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

于 2009-01-23T15:37:23.327 回答