sql-server – 过多的排序内存授予

为什么这个简单的查询被授予如此多的内存?
-- Demo table
CREATE TABLE dbo.Test
(
    TID integer IDENTITY NOT NULL,FilterMe integer NOT NULL,SortMe integer NOT NULL,Unused nvarchar(max) NULL,CONSTRAINT PK_dbo_Test_TID
    PRIMARY KEY CLUSTERED (TID)
);
GO
-- 100,000 example rows
INSERT dbo.Test WITH (TABLOCKX)
    (FilterMe,SortMe)
SELECT TOP (100 * 1000)
    CHECKSUM(NEWID()) % 1000,CHECKSUM(NEWID())
FROM sys.all_columns AS AC1
CROSS JOIN sys.all_columns AS AC2;
GO    
-- Query
SELECT
    T.TID,T.FilterMe,T.sortMe,T.Unused
FROM dbo.Test AS T 
WHERE 
    T.FilterMe = 567
ORDER BY 
    T.sortMe;

估计50行,优化器保留近500 MB的排序:

解决方法

这是sql Server中的一个错误(从2008年到2014年).

我的错误报告是here.

过滤条件作为残差谓词被下推到扫描操作符中,但是基于预过滤器基数估计错误地计算为该排序授予的存储器.

为了说明问题,我们可以使用(未​​记录和不支持的)跟踪标志9130来防止过滤器被压入扫描操作符.授予排序的内存现在正确地基于Filter输出的估计基数,而不是扫描:

SELECT
    T.TID,T.Unused
FROM dbo.Test AS T 
WHERE 
    T.FilterMe = 567
ORDER BY 
    T.sortMe
OPTION (QUERYTRACEON 9130); -- Not for production systems!

对于生产系统,需要采取措施以避免有问题的计划形状(将过滤器推入扫描并在另一列上进行排序).一种方法是提供过滤条件的索引和/或提供所需的排序顺序.

-- Index on the filter condition only
CREATE NONCLUSTERED INDEX IX_dbo_Test_FilterMe
ON dbo.Test (FilterMe);

使用此索引后,排序所需的内存授权仅为928KB:

更进一步,以下索引可以完全避免排序(零内存授权):

-- Provides filtering and sort order
-- nvarchar(max) column deliberately not INCLUDEd
CREATE NONCLUSTERED INDEX IX_dbo_Test_FilterMe_SortMe
ON dbo.Test (FilterMe,SortMe);

在以下sql Server x64 Developer Edition版本上进行了测试和错误确认:

2014   : 12.00.2430 (RTM CU4)
2012   : 11.00.5556 (SP2 CU3)
2008R2 : 10.50.6000 (SP3)
2008   : 10.00.6000 (SP4)

这在SQL Server 2016 Service Pack 1修复.发行说明包括以下内容

VSTS bug number 8024987
Table scans and index scans with push down predicate tend to overestimate memory grant for the parent operator.

经测试并确认固定于:

> Microsoft sql Server 2016(SP1) – 13.0.4001.0(X64)开发人员版
> Microsoft sql Server 2014(SP2-CU3)12.0.5538.0(X64)开发人员版

两种CE型号.

相关文章

SELECT a.*,b.dp_name,c.pa_name,fm_name=(CASE WHEN a.fm_n...
if not exists(select name from syscolumns where name=&am...
select a.*,pano=a.pa_no,b.pa_name,f.dp_name,e.fw_state_n...
要在 SQL Server 2019 中设置定时自动重启,可以使用 Window...
您收到的错误消息表明数据库 'EastRiver' 的...
首先我需要查询出需要使用SQL Server Profiler跟踪的数据库标...