由于 varchar(max) 对 tempdb 的溢出排序

Fre*_*gen 10 performance sql-server tempdb cardinality-estimates query-performance

在 32GB 的服务器上,我们运行 SQL Server 2014 SP2,最大内存为 25GB,我们有两个表,在这里您可以找到两个表的简化结构:

CREATE TABLE [dbo].[Settings](
    [id] [int] IDENTITY(1,1) NOT NULL,
    [resourceId] [int] NULL,
    [typeID] [int] NULL,
    [remark] [varchar](max) NULL,
    CONSTRAINT [PK_Settings] PRIMARY KEY CLUSTERED ([id] ASC)
) ON [PRIMARY]
GO

CREATE TABLE [dbo].[Resources](
    [id] [int] IDENTITY(1,1) NOT NULL,
    [resourceUID] [int] NULL,
 CONSTRAINT [PK_Resources] PRIMARY KEY CLUSTERED ([id] ASC)
) ON [PRIMARY]
GO
Run Code Online (Sandbox Code Playgroud)

具有以下非聚集索引:

CREATE NONCLUSTERED INDEX [IX_UID] ON [dbo].[Resources]
(
    [resourceUID] ASC
)

CREATE NONCLUSTERED INDEX [IX_Test] ON [dbo].[Settings]
(
    [resourceId] ASC,
    [typeID] ASC
)
Run Code Online (Sandbox Code Playgroud)

数据库配置为compatibility level120。

当我运行此查询时,会溢出到tempdb. 这是我执行查询的方式:

exec sp_executesql N'
select r.id,remark
FROM Resources r
inner join Settings on resourceid=r.id
where resourceUID=@UID
ORDER BY typeID',
N'@UID int',
@UID=38
Run Code Online (Sandbox Code Playgroud)

如果不选择该[remark]字段,则不会发生溢出。我的第一反应是溢出的发生是由于嵌套循环运算符上的估计行数较少。

因此,我将 5 个日期时间和 5 个整数列添加到设置表中,并将它们添加到我的选择语句中。当我执行查询时,没有发生溢出。

为什么只有在[remark]选择时才会发生溢出?这可能与这是一个varchar(max). 我该怎么做才能避免溢出tempdb

添加OPTION (RECOMPILE)到查询没有区别。

For*_*est 10

这里有几种可能的解决方法。

您可以手动调整内存授予,但我可能不会走那条路。

在获取最大长度列之前,您还可以使用 CTE 和 TOP 将排序推低。它看起来像下面这样。

WITH CTE AS (
SELECT TOP 1000000000 r.ID, s.ID AS ID2, s.typeID
FROM Resources r
inner join Settings s on resourceid=r.id
where resourceUID=@UID
ORDER BY s.typeID
)
SELECT c.ID, ca.remark
FROM CTE c
CROSS APPLY (SELECT remark FROM dbo.Settings s WHERE s.id = c.ID2) ca(remark)
ORDER BY c.typeID
Run Code Online (Sandbox Code Playgroud)

概念验证 dbfiddle在这里。示例数据仍将不胜感激!

如果您想阅读 Paul White 的出色分析,请阅读此处。


Jos*_*ell 7

为什么只有在选择 [备注] 时才会发生溢出?

当您包含该列时会发生溢出,因为您没有为正在排序的大字符串数据获得足够大的内存授权。

您没有获得足够大的内存授权,因为实际行数是估计行数的 10 倍(实际 1,302 对 126 估计)。

为什么估价不准?为什么 SQL Server 认为 dbo.Settings 中只有一行 aresourceid为 38?

这可能是一个统计问题,您可以通过运行进行检查DBCC SHOW_STATISTICS('dbo.Settings', 'IX_Test')并查看该直方图步骤的计数。但执行计划似乎表明统计数据尽可能完整和最新。

由于统计数据没有帮助,您最好的选择可能是重写查询 - Forrest他的回答中已经涵盖了这一点