为什么删除这些 LOWER 调用会像这样更改执行计划?

Mik*_*e B 4 sql-server sql-server-2012

在对慢速查询进行故障排除时,我被实际执行计划误导了一段时间,它告诉我在查询的后期排序,应该有几行占用了 > 80% 的查询时间。当时查询是在存储过程中动态构建的,大部分缓慢是由参数嗅探引起的,但很长一段时间我试图弄清楚为什么对这些行进行排序需要这么长时间。

这是查询:

SELECT Id
 , FirstName
 , LastName
 , FullName
 , DateOfBirth
 , CityStateZip
 , DriversLicenseState
 , DriversLicenseNumber
FROM (SELECT c.EntityId Id
       , p.FirstName
       , p.LastName
       , p.FullName
       , c.DateOfBirth
       , c.UpdateDate
       , CityStateZip = CASE a.EntityAddressId
           WHEN NULL THEN NULL
           ELSE CONCAT(a.City, ', ', s.Code, ' ', a.Zip)
           END
       , ds.Value DriversLicenseState
       , dn.Value DriversLicenseNumber
       , ROW_NUMBER() OVER (
               PARTITION BY p.FirstName, p.LastName, c.DateOfBirth
                    , ds.Value, dn.Value 
               ORDER BY c.UpdateDate DESC) RowNum
  FROM Store.Customer c
  INNER JOIN Entity.Person p ON c.EntityId = p.EntityId
  LEFT JOIN Entity.EntityAddress a ON c.EntityId = a.EntityId
  LEFT JOIN Vendor.StateProvince s ON a.StateProvinceId = s.StateProvinceId
  LEFT JOIN (
         SELECT ca.CustomerId, ca.Value
         FROM Store.CustomerAttribute ca
         INNER JOIN Models.Attribute a ON ca.AttributeId = a.AttributeId
         WHERE a.AttributeCode = 'DriversLicenseState') as ds
             ON c.EntityId = ds.CustomerId
  LEFT JOIN (
         SELECT ca.CustomerId, ca.Value
         FROM Store.CustomerAttribute ca
         INNER JOIN Models.Attribute a ON ca.AttributeId = a.AttributeId
         WHERE a.AttributeCode = 'DriversLicenseNumber') as dn 
             ON c.EntityId = dn.CustomerId
  WHERE LOWER(FirstName) LIKE '%bob%'
    AND LOWER(LastName) LIKE '%smith%'
    AND a.EntityAddressTypeId = 0 ) c
WHERE RowNum = 1
ORDER BY UpdateDate DESC
Run Code Online (Sandbox Code Playgroud)

这是执行计划的相关部分:

LOWER 的执行计划

请注意估计行数和实际行数之间的巨大差异。

如果我从内部 WHERE 子句中删除 2 个 LOWER 调用并且不更改其他任何内容,则执行计划步骤相同,但步骤的成本不同。执行计划的相同部分:

没有 LOWER 的执行计划

我在执行之间清除了计划缓存。这是一个错误吗?

Mar*_*ith 10

SQL Server以查询可用的尝试形式维护有关字符串列中子字符串的统计信息LIKE

有关这方面的更多信息,请参阅字符串摘要统计信息部分。

将列包装在函数调用中很可能会阻止任何嗅探参数值的尝试,并使用列上的统计信息来估计匹配行的数量,因此它依赖于猜测。不同的基数估计会导致不同的执行计划。