更新统计信息后查询计划不会失效

Fre*_*gen 3 statistics extended-events sql-server-2016

我正在运行一些测试以找出更新统计信息后查询计划何时失效。我用于测试的机器是 SQL Server Developer 2016 SP1 CU7。

我在BrentOzar.com上找到了一篇关于如何跟踪重新编译的文章,但我从未得到重新编译。 Auto Update Statistics并且Auto Create Statistics都启用

这是我的测试:

/* Create a table and put over 1k rows in it (to get past the 500 row stats threshold) */
CREATE TABLE dbo.MyTable (ID INT IDENTITY(1,1) PRIMARY KEY CLUSTERED, StringField VARCHAR(50));
GO
INSERT INTO dbo.MyTable(StringField) 
SELECT 'Stuff' FROM sys.all_objects;
GO

/* Start a trace monitoring recompiles */
exec sp_BlitzTrace @Action='start', @TargetPath='c:\temp\', @SessionId=@@SPID, @TraceRecompiles=1;
GO
SELECT * FROM dbo.MyTable WHERE StringField = 'NoRecordsHere';
GO
/* Increment the row mod counters to encourage a stats update */
BEGIN TRAN
DELETE dbo.MyTable;
GO
ROLLBACK
GO
UPDATE STATISTICS dbo.MyTable WITH fullscan
GO

/* We don't strictly need to wait, but makes different executions easier to see: */
WAITFOR DELAY '00:00:10';
GO
SELECT * FROM dbo.MyTable WHERE StringField = 'NoRecordsHere';
GO
EXEC sp_BlitzTrace @Action='stop'
GO
EXEC sp_BlitzTrace @Action='read'
GO
Run Code Online (Sandbox Code Playgroud)

据我了解,当您调用UPDATE STATISTICS WITH FULLSCAN并且行更改时,该计划应该会失效。或者我错过了什么?

Bre*_*zar 7

您的原始示例代码不可重新运行(表创建不在其中,或行数不存在),因此我重写了它以充实它并使其可重新运行。

问题是您的查询非常简单:统计数据是否更新并不重要,它会产生相同的计划。此调整将使您的 SELECT 查询绕过微不足道的优化:

SELECT * FROM dbo.MyTable WHERE StringField = 'NoRecordsHere' AND 1 = (SELECT 1);
Run Code Online (Sandbox Code Playgroud)

再次尝试测试:

/* Start a trace monitoring recompiles */
exec sp_BlitzTrace @Action='start', @TargetPath='c:\temp\', @SessionId=@@SPID, @TraceRecompiles=1;
GO
SELECT * FROM dbo.MyTable WHERE StringField = 'NoRecordsHere' AND 1 = (SELECT 1);
GO
/* Increment the row mod counters to encourage a stats update */
BEGIN TRAN
DELETE dbo.MyTable;
GO
ROLLBACK
GO
UPDATE STATISTICS dbo.MyTable WITH fullscan
GO

/* We don't strictly need to wait, but makes different executions easier to see: */
WAITFOR DELAY '00:00:10';
GO
SELECT * FROM dbo.MyTable WHERE StringField = 'NoRecordsHere' AND 1 = (SELECT 1);
GO
EXEC sp_BlitzTrace @Action='stop'
GO
EXEC sp_BlitzTrace @Action='read'
GO
Run Code Online (Sandbox Code Playgroud)

现在你得到了重新编译——注意显示 recompile_cause = "Statistics changed" 的第三行

重新编译

这是sp_BlitzCache(免责声明:我是该开源脚本的一个非常小的作者)显示琐碎计划警告的原因之一 - 您无法获得完全优化计划中获得的所有酷东西,例如缺少索引建议。

我当然不是说在您的查询中添加“WHERE 1 = (SELECT 1)”是个好主意——只是说在构建重现场景时,您必须注意琐碎的查询。要了解您的查询是否非常简单,请查看执行计划,右键单击根节点(如 SELECT),单击属性,然后查看优化级别。如果它说 Trivial,那么你就没有得到微软那些人的全部天才。