【问题标题】:What are some best practices for optimizing multiple column LIKE SQL queries?优化多列 LIKE SQL 查询的一些最佳实践是什么?
【发布时间】:2010-10-03 03:50:20
【问题描述】:

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

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

【问题讨论】:

    标签: sql search optimization sql-like


    【解决方案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】:

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

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

      【讨论】:

        【解决方案3】:
        • 避免使用“OR” - 它们通常会阻止使用索引
        • 切勿在左侧放置“%”。 - 同样的原因。

        【讨论】:

        • 是的,它可能会限制您的搜索,但 LIKE '% 会破坏性能,如果这是一个问题,是时候升级您的服务器,或者至少将 I/O 升级到无光纤存储。跨度>
        • 根据我提供的条件组合执行大量 IF 语句来构建查询会更好吗?看起来有点矫枉过正,需要大量的代码复制和粘贴,但我愿意尝试不同的方法来改进它。
        【解决方案4】:

        我会尽量不让我的 sql 代码添加“%”,而是希望参数已经包含它,这当然是在您在应用程序中验证它之后!然后不要包含 '=' 比较,始终使用 LIKE:

        WHERE (cu.LastName LIKE @LastName)

        代替:

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

        【讨论】:

        • 为什么LastName = @LastName?如果 LastName = @LastName 那么 LastName LIKE LastName + '%' 的评估结果也为真
        【解决方案5】:

        这不是整行

        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 + '%'
        

        【讨论】:

        • 好问题。我需要再次查找 SQL OR 以查看它是否评估双方而不考虑左侧的返回。
        • 实际上有一个区别,@Zip = '' 也会在 a.Zip 中返回 NULL 值,这就是你想要的吗?
        • @City/@State/@Zip IS NULL 或空逻辑在问题中的查询之前已解决和更改,因此 @Zip='' 有效。
        猜你喜欢
        • 1970-01-01
        • 2014-01-18
        • 1970-01-01
        • 2021-01-06
        • 1970-01-01
        • 1970-01-01
        • 2013-01-06
        • 1970-01-01
        • 2020-08-23
        相关资源
        最近更新 更多