为什么 Concatenation 运算符估计的行数少于其输入?

Geo*_*son 20 performance sql-server concat sql-server-2014 cardinality-estimates query-performance

在下面的查询计划片段中,很明显Concatenation运算符的行估计应该是~4.3 billion rows,或者它的两个输入的行估计的总和。

但是,~238 million rows会产生的估计值,从而导致将数百 GB 数据溢出到 tempdb的次优Sort/Stream Aggregate策略。在这种情况下,逻辑上一致的估计会产生Hash Aggregate,消除溢出并显着提高查询性能。

这是 SQL Server 2014 中的错误吗?是否存在任何有效情况下低于输入值的估计是合理的?可能有哪些解决方法?

在此处输入图片说明

这是完整的查询计划(匿名)。我没有系统管理员访问此服务器的权限以提供来自QUERYTRACEON 2363或类似跟踪标志的输出,但如果它们有帮助,我可以从管理员那里获取这些输出。

数据库的兼容性级别为 120,因此使用新的 SQL Server 2014 Cardinality Estimator。

每次加载数据时都会手动更新统计信息。鉴于数据量,我们目前使用默认采样率。较高的采样率(或FULLSCAN)可能会产生影响。

Pau*_*ite 21

这个 Connect 项目上引用 Campbell Fraser 的话:

这些“基数不一致”可能出现在许多情况下,包括使用 concat 时。它们之所以会出现,是因为对最终计划中特定子树的估计可能是在不同结构但逻辑上等效的子树上执行的。由于基数估计的统计性质,不能保证对不同但逻辑等效的树进行估计得到相同的估计。所以总体上没有提供预期一致性的保证。

稍微扩展一下:我喜欢解释的方式是说初始基数估计(在基于成本的优化开始之前执行)产生更“一致”的基数估计,因为整个初始树被处理,每个后续估计直接取决于前一个。

在基于成本的优化过程中,计划树的一部分(一个或多个运算符)可能会被探索并替换为备选方案,每个备选方案都可能需要新的基数估计。没有一般的方法可以说哪个估计通常比另一个更好,因此很可能最终得到一个看起来“不一致”的最终计划。这只是将“一些计划”拼接在一起以形成最终安排的结果。

说了这么多,有在SQL Server 2014中引入的新基数估计(CE),使一些这方面的细节变化有些不太常见的比是与原行政长官的情况。

除了升级到最新的累积更新并检查 4199 的优化器修复是否打开之外,您的主要选择是尝试统计/索引更改(注意缺少索引的警告)和更新,或以不同方式表达查询。目标是获得一个显示您所需行为的计划。例如,这可以用计划指南来冻结。

匿名计划使评估细节变得困难,但我也会仔细查看位图,看看它们是“优化”(Opt_Bitmap) 还是优化后 (Bitmap) 种类。我也怀疑过滤器。

如果行数准确无误,这似乎是一个可能受益于列存储的查询。除了通常的好处之外,您还可以利用批处理模式运算符的动态内存授予(可能需要跟踪标志 9389)。


Han*_*non 7

在 SQL Server 2012 (11.0.6020) 上构建一个公认的相当简单的测试平台允许我重新创建一个计划,其中两个哈希匹配的查询通过UNION ALL. 我的测试台没有显示您看到的错误估计。也许这SQL Server 2014 CE 问题。

对于实际返回 280 行的查询,我得到了 133.785 行的估计值,但是这是可以预料的,因为我们将在下面进一步看到:

IF OBJECT_ID('dbo.Union1') IS NOT NULL
DROP TABLE dbo.Union1;
CREATE TABLE dbo.Union1
(
    Union1_ID INT NOT NULL
        CONSTRAINT PK_Union1
        PRIMARY KEY CLUSTERED
        IDENTITY(1,1)
    , Union1_Text VARCHAR(255) NOT NULL
    , Union1_ObjectID INT NOT NULL
);

IF OBJECT_ID('dbo.Union2') IS NOT NULL
DROP TABLE dbo.Union2;
CREATE TABLE dbo.Union2
(
    Union2_ID INT NOT NULL
        CONSTRAINT PK_Union2
        PRIMARY KEY CLUSTERED
        IDENTITY(2,2)
    , Union2_Text VARCHAR(255) NOT NULL
    , Union2_ObjectID INT NOT NULL
);

INSERT INTO dbo.Union1 (Union1_Text, Union1_ObjectID)
SELECT o.name, o.object_id
FROM sys.objects o;

INSERT INTO dbo.Union2 (Union2_Text, Union2_ObjectID)
SELECT o.name, o.object_id
FROM sys.objects o;
GO

SELECT *
FROM dbo.Union1 u1
    INNER HASH JOIN sys.objects o ON u1.Union1_ObjectID = o.object_id
UNION ALL
SELECT *
FROM dbo.Union2 u2
    INNER HASH JOIN sys.objects o ON u2.Union2_ObjectID = o.object_id;
Run Code Online (Sandbox Code Playgroud)

认为原因在于缺乏联合的两个结果连接的统计数据。在大多数情况下,当面临缺乏统计数据时,SQL Server 需要对列的选择性进行有根据的猜测。

乔袋有一个有趣的阅读这里

对于 a UNION ALL,可以肯定地说,我们将准确地看到联合的每个组件返回的总行数,但是由于 SQL Server对 的两个组件使用行估计UNION ALL,我们看到它添加了来自两个组件的总估计查询以得出连接运算符的估计值。

在我上面的例子中,每个部分的估计行数UNION ALL是 66.8927,当总和等于 133.785,我们看到连接运算符的估计行数。

上面联合查询的实际执行计划如下所示:

在此处输入图片说明

您可以看到“估计”与“实际”的行数。在我的情况下,添加两个哈希匹配运算符返回的“估计”行数完全等于串联运算符显示的数量。

我会尝试从您在问题中显示的 Paul White 的帖子中推荐的跟踪 2363 等获取输出。或者,您可以尝试OPTION (QUERYTRACEON 9481)在查询中使用以恢复到 70 CE 版本,以查看是否“修复”了问题。