我遇到了一些性能问题。
我有一张大约有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
答案 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%的表格符合资格。
具有字面值的计划应该有更好的估计。您提供的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
。答案 1 :(得分:0)
也许这2个问题可以让您更好地了解LIKE
和=
运营商所涉及的性能问题: