带/不带参数的性能问题

时间:2012-09-17 08:40:22

标签: sql sql-server performance tsql

我遇到了一些性能问题。

我有一张大约有200万行的表。

CREATE TABLE [dbo].[M8](
    [M8_ID] [int] IDENTITY(1,1) NOT NULL,
    [APPLIC] [char](8) NOT NULL,
    [NIVALERTE] [numeric](1, 0) NOT NULL,
    [LOGDH] [datetime2](7) NULL,
    [USERX] [char](20) NOT NULL,
    [TACHE] [char](3) NOT NULL,
    [PRG] [char](32) NOT NULL,
    [DOS] [numeric](3, 0) NOT NULL,
    [ERRNUM] [numeric](5, 0) NOT NULL,
    [LOGTXT] [char](200) NOT NULL)

我用C#和ADO.NET

阅读它们

在管理工作室(SQL Server 2008 R2)中,使用该查询:

SELECT 
    M8.M8_ID, M8.APPLIC, M8.NIVALERTE, M8.LOGDH, M8.USERX, M8.TACHE, 
    M8.PRG, M8.DOS, M8.ERRNUM, M8.LOGTXT
FROM 
    M8 AS M8 WITH(NOLOCK) 
WHERE 
    ((M8.APPLIC LIKE 'DAV' ) )
ORDER BY 
    M8.LOGDH DESC, M8.M8_ID ASC
OPTION (FAST 1)

第一行需要大约1分钟。

但是,

DECLARE @APPLIC_ZOOMAPRESCLE_ZOOM_LIKE_APPLIC_WHERE_0 as char(8) = 'DAV'

SELECT 
   M8.M8_ID, M8.APPLIC, M8.NIVALERTE, M8.LOGDH, M8.USERX, M8.TACHE, 
   M8.PRG, M8.DOS, M8.ERRNUM, M8.LOGTXT
FROM 
   M8 AS M8 WITH(NOLOCK) 
WHERE 
   ((M8.APPLIC LIKE @APPLIC_ZOOMAPRESCLE_ZOOM_LIKE_APPLIC_WHERE_0 ) )
ORDER BY 
   M8.LOGDH DESC, M8.M8_ID ASC
OPTION(FAST 1)

我在4秒后得到第一行。

PS:我知道,我没有%。

编辑:以下是执行计划https://www.dropbox.com/sh/jgai5f9txbs84x6/EP5_hj8DNv

2 个答案:

答案 0 :(得分:5)

你的桌子有1,517,820行。其中近三分之一(476,672)包含值DAV(或更准确地说,值DAV     ,因为它是CHAR(8)数据类型,因此用尾随空格填充。

LIKE比较中,match_expression中的尾随空格并不重要(尽管它们在pattern本身中很重要)。

因此,表达式WHERE APPLIC LIKE 'DAV'实际上匹配476,672行。然而,这两个执行计划都没有估计到这附近。虽然更快的计划(带变量)更接近三个数量级。

+-----------------------+-----------+-----------+
|                       | Slow Plan | Fast Plan |
+-----------------------+-----------+-----------+
| Estimated # Rows      | 32        | 47,343    |
| Memory Grant          | 1 MB      | 333 MB    |
| Degree of Parallelism | 1         | 4         |
+-----------------------+-----------+-----------+

对于带有变量的计划,因为SQL Server没有进行变量嗅探(没有例如OPTION (RECOMPILE)提示),它会回过头来猜测有多少行会与谓词匹配并得出一个估计值3.1%的表格符合资格。

快速计划

Fast

具有字面值的计划应该有更好的估计。您提供的DBCC SHOW_STATISTICS输出的屏幕截图(添加了另外一百万行后)显示DAV肯定在那里

Stats

不幸的是,虽然列值中的尾随空格对于查询结果并不重要,但它们的存在确实搞乱了基数估计(Reported as a bug here并且当前声明在下一版本中已修复)。由于这个问题,它估计只返回少量行,并提出以下计划。

慢速计划

Slow

除了执行50万次密钥查找之外,由于基数估计不佳,内存授权可能不足以满足排序数据的大小,导致溢出到tempdb

如果您可以更改查询或表架构,可以考虑许多解决方法。

  • 使用=代替LIKE
  • WHERE子句更改为LIKE CAST('DAV' AS CHAR(8))
  • 将列数据类型更改为VARCHAR(8)(并确保修剪所有存储的值)。
  • 删除当前正在搜索的索引(Index_A)。您尚未提供其定义,但如果它是具有少量不同值的列上的单个列索引,则它的存在可能更多是障碍而不是帮助(取决于您的查询工作负载)。)
  • 使用键列APPLIC添加覆盖索引(可能LOGDH DESC, M8_ID ASC以避免排序),其他引用的列为INCLUDED

答案 1 :(得分:0)

也许这2个问题可以让您更好地了解LIKE=运营商所涉及的性能问题: