标签: cardinality-estimates

SQL Server 2014:对不一致的自连接基数估计有什么解释?

考虑 SQL Server 2014 中的以下查询计划:

在此处输入图片说明

在查询计划中,自联接ar.fId = ar.fId产生 1 行的估计值。然而,这是一个逻辑上不一致的估计:ar20,608行和只有一个不同的值fId(准确地反映在统计数据中)。因此,此连接会生成行 ( ~424MMrows)的完整叉积,从而导致查询运行数小时。

我很难理解为什么 SQL Server 会得出一个很容易证明与统计数据不一致的估计值。有任何想法吗?

初步调查和其他细节

根据 Paul在此处回答,用于估计连接基数的 SQL 2012 和 SQL 2014 启发式方法似乎都应该可以轻松处理需要比较两个相同直方图的情况。

我从跟踪标志 2363 的输出开始,但不太容易理解。请问下面的片段意味着SQL Server在比较直方图fIdbId以估计选择性的只加入使用fId?如果是这样,那显然是不正确的。还是我误读了跟踪标志输出?

Plan for computation:
  CSelCalcExpressionComparedToExpression( QCOL: [ar].fId x_cmpEq QCOL: [ar].fId )
Loaded histogram for column QCOL: [ar].bId from stats with id 3
Loaded histogram for column QCOL: [ar].fId from stats with id 1 …
Run Code Online (Sandbox Code Playgroud)

performance sql-server execution-plan sql-server-2014 cardinality-estimates query-performance

28
推荐指数
1
解决办法
759
查看次数

为什么 LEN() 函数严重低估了 SQL Server 2014 中的基数?

我有一个带有字符串列的表和一个检查具有特定长度的行的谓词。在 SQL Server 2014 中,无论我检查的长度如何,我都会看到估计为 1 行。这产生了非常糟糕的计划,因为实际上有数千甚至数百万行,而 SQL Server 选择将此表放在嵌套循环的外侧。

对于 SQL Server 2014 的基数估计为 1.0003 而 SQL Server 2012 估计为 31,622 行,是否有解释?有没有好的解决方法?

以下是该问题的简短再现:

-- Create a table with 1MM rows of dummy data
CREATE TABLE #customers (cust_nbr VARCHAR(10) NOT NULL)
GO

INSERT INTO #customers WITH (TABLOCK) (cust_nbr)
    SELECT TOP 1000000 
        CONVERT(VARCHAR(10),
        ROW_NUMBER() OVER (ORDER BY (SELECT NULL))) AS cust_nbr
    FROM master..spt_values v1
    CROSS JOIN master..spt_values v2
GO

-- Looking for string of a certain length.
-- While both CEs …
Run Code Online (Sandbox Code Playgroud)

sql-server varchar functions sql-server-2014 cardinality-estimates

26
推荐指数
2
解决办法
1486
查看次数

为什么子查询将行估计减少到 1?

考虑以下人为但简单的查询:

SELECT 
  ID
, CASE
    WHEN ID <> 0 
    THEN (SELECT TOP 1 ID FROM X_OTHER_TABLE) 
    ELSE (SELECT TOP 1 ID FROM X_OTHER_TABLE_2) 
  END AS ID2
FROM X_HEAP;
Run Code Online (Sandbox Code Playgroud)

我希望此查询的最终行估计值等于X_HEAP表中的行数。无论我在子查询中做什么,对于行估计都无关紧要,因为它无法过滤掉任何行。但是,在 SQL Server 2016 上,由于子查询,我看到行估计值减少到 1:

错误查询

为什么会发生这种情况?我该怎么办?

使用正确的语法很容易重现这个问题。这是一组可以执行此操作的表定义:

CREATE TABLE dbo.X_HEAP (ID INT NOT NULL)
CREATE TABLE dbo.X_OTHER_TABLE (ID INT NOT NULL);
CREATE TABLE dbo.X_OTHER_TABLE_2 (ID INT NOT NULL);

INSERT INTO dbo.X_HEAP WITH (TABLOCK)
SELECT TOP (1000) ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
FROM master..spt_values;

CREATE STATISTICS X_HEAP__ID ON X_HEAP …
Run Code Online (Sandbox Code Playgroud)

sql-server sql-server-2016 cardinality-estimates

26
推荐指数
2
解决办法
1177
查看次数

LIKE 运算符的基数估计(局部变量)

我的印象是,当LIKE在所有针对未知场景的优化中使用运算符时,旧版和新版 CE 都使用 9% 的估计值(假设相关统计数据可用并且查询优化器不必求助于选择性猜测)。

当对信用数据库执行以下查询时,我在不同的 CE 下得到不同的估计。在新的 CE 下,我收到了我期望的 900 行的估计值,在旧版 CE 下,我收到了 241.416 的估计值,但我无法弄清楚这个估计值是如何得出的。有没有人能够发光?

-- New CE (Estimate = 900)
DECLARE @LastName VARCHAR(15) = 'BA%'
SELECT * FROM [Credit].[dbo].[member]
WHERE [lastname] LIKE @LastName;

-- Forcing Legacy CE (Estimate = 241.416)
DECLARE @LastName VARCHAR(15) = 'BA%'
SELECT * FROM [Credit].[dbo].[member]
WHERE [lastname] LIKE @LastName
OPTION (
QUERYTRACEON 9481,
QUERYTRACEON 9292,
QUERYTRACEON 9204,
QUERYTRACEON 3604
);
Run Code Online (Sandbox Code Playgroud)

在我的场景中,我已经将信用数据库设置为兼容性级别 120,因此为什么在第二个查询中我使用跟踪标志来强制使用旧版 CE 并提供有关查询优化器使用/考虑的统计信息的信息。我可以看到正在使用有关“姓氏”的列统计信息,但我仍然无法弄清楚 241.416 的估计值是如何得出的。

除了这篇 Itzik Ben-Gan 文章之外,我在网上找不到任何其他 …

sql-server optimization statistics sql-server-2014 cardinality-estimates

24
推荐指数
2
解决办法
2258
查看次数

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

在下面的查询计划片段中,很明显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)可能会产生影响。

performance sql-server concat sql-server-2014 cardinality-estimates query-performance

20
推荐指数
2
解决办法
783
查看次数

为什么这个连接基数估计这么大?

对于以下查询,我遇到了我认为不可能高的基数估计:

SELECT dm.PRIMARY_ID
FROM
(
    SELECT COALESCE(d1.JOIN_ID, d2.JOIN_ID, d3.JOIN_ID) PRIMARY_ID
    FROM X_DRIVING_TABLE dt
    LEFT OUTER JOIN X_DETAIL_1 d1 ON dt.ID = d1.ID
    LEFT OUTER JOIN X_DETAIL_LINK lnk ON d1.LINK_ID = lnk.LINK_ID
    LEFT OUTER JOIN X_DETAIL_2 d2 ON dt.ID = d2.ID
    LEFT OUTER JOIN X_DETAIL_3 d3 ON dt.ID = d3.ID
) dm
INNER JOIN X_LAST_TABLE lst ON dm.PRIMARY_ID = lst.JOIN_ID;
Run Code Online (Sandbox Code Playgroud)

估计的计划在这里。我正在处理表格的统计副本,因此无法包含实际计划。但是,我不认为它与这个问题非常相关。

SQL Server 估计将从“dm”派生表返回 481577 行。然后估计在连接到 X_LAST_TABLE 后将返回 4528030000 行,但 JOIN_ID 是 X_LAST_TIME 的主键。我希望连接基数估计在 0 到 481577 行之间。相反,当交叉连接外部表和内部表时,行估计值似乎是我得到的行数的 10%。计算结果为四舍五入:481577*94025*0.1 …

sql-server sql-server-2012 cardinality-estimates

20
推荐指数
2
解决办法
1721
查看次数

查询计划“基数估计”中的警告

create table T(ID int identity primary key)
insert into T default values
insert into T default values

go

select cast(ID as varchar(10)) as ID
from T
where ID = 1
Run Code Online (Sandbox Code Playgroud)

上面的查询在查询计划中有一个警告。

<Warnings>
  <PlanAffectingConvert ConvertIssue="Cardinality Estimate" Expression="CONVERT(varchar(10),[xx].[dbo].[T].[ID],0)" />
</Warnings>
Run Code Online (Sandbox Code Playgroud)

为什么它有警告?

字段列表中的强制转换如何影响基数估计?

sql-server execution-plan type-conversion sql-server-2012 cardinality-estimates

19
推荐指数
1
解决办法
1万
查看次数

SQL Server 2014 COUNT(DISTINCT x) 忽略列 x 的统计密度向量

对于一个COUNT(DISTINCT)具有约 10 亿个不同值的查询计划,我得到了一个散列聚合估计只有约 300 万行的查询计划。

为什么会这样?SQL Server 2012 产生了很好的估计,所以这是我应该在 Connect 上报告的 SQL Server 2014 中的错误吗?

查询和差估计

-- Actual rows: 1,011,719,166
-- SQL 2012 estimated rows: 1,079,130,000 (106% of actual)
-- SQL 2014 estimated rows: 2,980,240 (0.29% of actual)
SELECT COUNT(DISTINCT factCol5)
FROM BigFactTable
OPTION (RECOMPILE, QUERYTRACEON 9481) -- Include this line to use SQL 2012 CE

-- Stats for the factCol5 column show that there are ~1 billion distinct values
-- This is a good estimate, and it …
Run Code Online (Sandbox Code Playgroud)

sql-server sql-server-2014 cardinality-estimates

16
推荐指数
1
解决办法
630
查看次数

排序溢出到 tempdb 但估计行等于实际行

在最大内存设置为 25GB 的 SQL Server 2016 SP2 上,我们有一分钟执行大约 80 次的查询。该查询将大约 4000 页溢出到 tempdb。这会导致 tempdb 的磁盘上出现大量 IO。

当您查看查询计划(简化查询)时,您会看到估计行数等于实际行数,但仍然会发生溢出。所以过时的统计数据不能成为问题的原因。

我做了一些测试和以下查询溢出到 Tempdb:

select id --uniqueidentifier
from SortProblem
where [status] ='A'
order by SequenceNumber asc
option (maxdop 1)
Run Code Online (Sandbox Code Playgroud)

但是,如果我选择不同的列,则不会发生溢出:

select startdate --datetime
from SortProblem
where [status] ='A'
order by SequenceNumber asc 
option (maxdop 1)
Run Code Online (Sandbox Code Playgroud)

所以我试图“放大” id 列的大小:

select CONVERT(nvarchar(512),id)
from SortProblem
where [status] ='A'
order by SequenceNumber asc 
option (maxdop 1)
Run Code Online (Sandbox Code Playgroud)

然后也不会发生溢出。

为什么 uniqueidentifier 不会溢出到 tempdb 和 datatime 列?当我删除大约 20000 条记录时,当我选择 id …

sql-server tempdb sorting sql-server-2016 cardinality-estimates

15
推荐指数
1
解决办法
542
查看次数

直方图外的基数估计

设置

我在理解基数估计时遇到了一些麻烦。这是我的测试设置:

  • Stack Overflow 数据库 2010 版
  • SQL Server 2017 CU15+GDR (KB4505225) - 14.0.3192.2
  • 新 CE(兼容级别 140)

我有这个过程:

USE StackOverflow2010;
GO

CREATE OR ALTER PROCEDURE #sp_PostsByCommentCount
    @CommentCount int
AS
BEGIN
    SELECT * 
    FROM dbo.Posts p
    WHERE 
        p.CommentCount = @CommentCount
    OPTION (RECOMPILE); 
END;
GO
Run Code Online (Sandbox Code Playgroud)

dbo.Posts表上没有非聚集索引或统计信息(在 上有聚集索引Id)。

当为此要求估计计划时,“估计行数”dbo.Posts是 1,934.99:

EXEC #sp_PostsByCommentCount @CommentCount = 51;
Run Code Online (Sandbox Code Playgroud)

当我询问估计计划时,自动创建了以下统计对象:

DBCC SHOW_STATISTICS('dbo.Posts', [_WA_Sys_00000006_0519C6AF]);
Run Code Online (Sandbox Code Playgroud)

SSMS 中统计输出的屏幕截图

其中的亮点是:

  • 统计数据的采样率非常低,为 1.81% (67,796 / 3,744,192)
  • 仅使用了 31 个直方图步骤
  • “所有密度”值为0.03030303(采样了 33 个不同的值)
  • RANGE_HI_KEY直方图中的最后一个是 50,其中EQ_ROWS1 …

sql-server statistics database-internals cardinality-estimates sql-server-2017

15
推荐指数
1
解决办法
471
查看次数