SQL Server 2012 内连接估计行数问题

use*_*507 5 sql-server execution-plan sql-server-2012 cardinality-estimates

我有一个简单的连接,如下所示:

select * 
from fact_sales f
join dim_company d on f.company_SK = d.company_SK
Run Code Online (Sandbox Code Playgroud)

事实表包含略多于 5 亿条记录(带有 NC 列存储),查询将返回所有记录,但是,联接后的估计行数仅为 3 亿条。直到哈希连接,估计的行数是正确的,只有在连接之后才下降到 3 亿。这是查询的估计计划:

在此处输入图片说明

我已经更新了事实表(使用全扫描)和维度表中联接中使用的 SK 列的统计数据,这是每个表的直方图:

在此处输入图片说明

这个问题似乎只发生在数据库中的几个维度表上,加入其他维度表不会产生相同类型的基数估计问题 - 关于如何解决这个问题或进一步调查的任何建议?

如果我在查询中添加一个 where 子句,它会正确估计连接之前/之后的行数,例如

select * 
from fact_sales f
join dim_company d on f.company_SK = d.company_SK where company_SK = 1
Run Code Online (Sandbox Code Playgroud)

将估计来自连接的 467,583,000 行,这与直方图中的内容相匹配。

该问题似乎仅在查询中没有任何过滤器时才会发生。它在更大的查询中导致问题(排序溢出)。我已经将范围缩小到这个特定的连接。

我确实有一个 FK 约束,但它们WITH NOCHECK在事实表上已被关闭 ( )(我们被告知关闭它们以便 ETL 可以更快)。不幸的是,重新打开 FK 不是一种选择:(

更新:启用跟踪标志 2301 解决了这个问题:p

Joe*_*ish 3

我能够在启用旧版 CE 和 TF 4199 的 SQL Server 2014 中重现您的问题。我使用行存储事实表,因为我对列存储缺乏经验。

对于使用过滤器的查询,查询优化器将查询重写为如下所示:

select * 
from fact_sales f
join dim_company d on f.company_SK = d.company_SK 
where f.company_SK = 1 and d.company_SK = 1
Run Code Online (Sandbox Code Playgroud)

对于该查询,优化器可以直接对两个表使用包含 1 的直方图步骤,您将得到 467,583,000 X 1 作为估计行数。

没有过滤器的查询将使用线性插值

对于具有单个等式或不等式谓词的连接,旧版 CE 通过使用线性插值逐步对齐两个直方图来连接连接列上的直方图。

请注意,维度表的统计信息没有 RANGE_HI_KEY 值为 1 的直方图步骤,而几乎所有数据都位于事实表中。该值的线性插值步骤出现问题。如果我创建的维度表没有 0 值,则直方图将获得 RANGE_HI_KEY 值为 1 的步骤。这将修复估计值。简而言之,你的直方图不走运。我认为没有一种受支持的方法可以强制维度表上的直方图包含 1 的步骤,除非您愿意向查询添加其他过滤器。

我不知道你的数据的确切规则,但我可以给你两个解决方法。第一个解决方法是为查询启用跟踪标志 2301。这修复了这部分查询的估计,但可能会对查询的其余部分产生其他负面影响。此跟踪标志由 MS记录,但在使用它之前请咨询您的 DBA。

第二个解决方法是鼓励查询优化器对连接使用不同的基数估计方法。以下查询为我提供了一个很好的估计,并且只对事实表进行一次扫描:

select * 
from FACT_SALES f
join (SELECT COMPANY_SK FROM DIM_COMPANY UNION SELECT 1) d
on f.company_SK = d.company_SK;
Run Code Online (Sandbox Code Playgroud)