【问题标题】:Performance issue with/without parameters带/不带参数的性能问题
【发布时间】:2012-09-17 08:40:21
【问题描述】:

我有一些性能问题。

我有一个大约有 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

【问题讨论】:

  • 你是按这个顺序运行它们的吗?
  • 那如果不需要的话最好用=代替like。
  • @Jodrell : 我清除了它们之间的缓存
  • @AndrásOttó:我知道,但这是一个通用查询。在那个程序中,我不能...

标签: sql sql-server performance tsql


【解决方案1】:

您的表格有 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% 的估计值表中的人将符合条件。

快速计划

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

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

慢计划

除了由于基数估计差而执行 50 万个键查找之外,内存授予可能远远不足以满足要排序的数据大小,从而导致溢出到tempdb

如果您可以更改查询或表架构,您可能会考虑许多变通方法。

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

【讨论】:

  • 非常感谢您提供这些解释和解决方法。我想我要么添加一个 %,因为 DB2 = 'DAV' 不返回 'DAV ' 或将 'DAV' 转换为 char(8)。
  • @Xavinou - 我不建议使用 % 的原因是,如果您(在未来某个时间)需要另一个 APPLIC 值,它可能会改变语义与DAV。它也会带回这些行。
【解决方案2】:

也许这2个问题会让你更好地理解LIKE=操作符所涉及的性能问题:

【讨论】:

  • 谢谢,但这并不是我真正感兴趣的 like 或 (=) 的使用,而是使用 DECLARE 或不使用 DECLARE 的性能问题
  • @Xavinou 但阅读这些文章可能会让您有所了解。我相信当比较的右侧是正确的长度时,引擎会将其视为等于并使用索引。当它更短时,它不会。
  • @Jodrell - 如果没有前导通配符,LIKE 可以使用范围搜索。即使使用前导通配符,它​​也可以使用范围搜索,但范围是整个索引。问题出在 OP 怀疑的 DECLARE 上。 SQL Server 不进行变量嗅探,因此不知道查询的选择性。
  • @Jodrell - True 没有发现 OP 说可变版本更快。他们需要发布计划,以便我们看到差异。但是是的,SQL Server 编译一个不考虑实际变量值的计划,除非在分配变量后该语句需要重新编译。
  • @Jodrell - 不确定您是否查看了计划,但事实证明缓慢的 实际上使用了索引,这就是问题所在。最快的会进行扫描。在 OP 的情况下,它们有许多行的值为 DAV,即使 these are present in the stats 的统计估计 LIKE 似乎没有意识到这些值将匹配谓词。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-08-19
  • 1970-01-01
  • 2011-01-02
  • 1970-01-01
相关资源
最近更新 更多