如何提高新插入数据联接中 1 行的行估计值

cro*_*sek 2 sql-server optimization statistics cardinality-estimates

表的 CacheId 列存在自定义统计信息。隔夜统计更新后:

Statistics for INDEX 'ST_TableName_CacheId'.
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

Name                            Updated                         Rows                            Rows Sampled                    Steps                           Density                         Average Key Length              String Index                    
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
ST_TableName_CacheId Apr 26 2014  2:04AM             121482                          121482                          6                               0                               4                               NO                                                              121482                          

All Density                     Average Length                  Columns                         
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
0.1666667                       4                               CacheId                         

Histogram Steps                 
RANGE_HI_KEY                    RANGE_ROWS                      EQ_ROWS                         DISTINCT_RANGE_ROWS             AVG_RANGE_ROWS                  
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
39968                           0                               20247                           0                               1                               
40058                           0                               20247                           0                               1                               
40062                           0                               20247                           0                               1                               
40066                           0                               20247                           0                               1                               
40069                           0                               20247                           0                               1                               
41033                           0                               20247                           0                               1                               
Run Code Online (Sandbox Code Playgroud)

1) 针对此表中现有数据集的连接性能,其中 CacheId = 41033 表现良好,具有良好的估计(23622 与 20247 的实际值)。

2) 然后使用 CacheId = 41273 的 20247 行执行插入。

3) 然后对这个新插入的数据集的连接显示对 1 行的错误估计导致计划错误。

4) 手动更新统计数据(最初使用全扫描)显示了一个新的直方图:

Statistics for INDEX 'ST_TableName_CacheId'.
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

Name                            Updated                         Rows                            Rows Sampled                    Steps                           Density                         Average Key Length              String Index                    
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
ST_TableName_CacheId Apr 28 2014 10:41AM             141729                          141729                          7                               0                               4                               NO                                                              141729                          

All Density                     Average Length                  Columns                         
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
0.1428571                       4                               CacheId                         

Histogram Steps                 
RANGE_HI_KEY                    RANGE_ROWS                      EQ_ROWS                         DISTINCT_RANGE_ROWS             AVG_RANGE_ROWS                  
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
39968                           0                               20247                           0                               1                               
40058                           0                               20247                           0                               1                               
40062                           0                               20247                           0                               1                               
40066                           0                               20247                           0                               1                               
40069                           0                               20247                           0                               1                               
41033                           0                               20247                           0                               1                               
41274                           0                               20247                           0                               1                               
Run Code Online (Sandbox Code Playgroud)

5) 对 CacheId = 41274 再次运行相同的连接查询显示完美的估计 (20247) 和良好的性能。

Q1)为什么最初的估计在数学上如此糟糕?我的意思是 CacheId 是稀疏的,但不是 20000:1 的比率。

Q2)随着 cacheId 数量的增加,您是否期望新插入数据的估计值自然提高?

Q3)是否有任何方法(吞咽,技巧或其他)来改进估计(或使其不太确定 1 行),而不必在每次插入新数据集时更新统计信息(例如在一个更大的 CacheId = 999999)。

以下是表中所有 CacheId 的真实行数:

CacheId Rows
39968   20247
40058   20247
40062   20247
40066   20247
40069   20247
41033   20247
41274   20247
Run Code Online (Sandbox Code Playgroud)

[我认为不需要 QP 来回答这个问题,并且需要一些工作来清理它们。如果需要,我可以回答具体问题!]

小智 5

Q1) 为什么最初的估计在数学上如此糟糕?我的意思是 CacheId 是稀疏的,但不是 20000:1 的比率。

这是在 SQL Server 中触发自动更新统计信息统计维护功能 (autostats)的规则:

上面的算法可以总结成一个表格:


表类型 | 空状态 | 空时的阈值|非空时的阈值


永久 | < 500 行 | # of Change >= 500 | # of Changes >= 500 + (20% of Cardinality)

即使认为 KB 指向 2000,直到 2012 年仍然如此。

运行此场景并亲眼看看。

第1步

SET STATISTICS IO OFF;
GO
SET NOCOUNT ON;
GO
-- make sure the Include Actual Execution Plan is off!!!
IF OBJECT_ID('IDs') IS NOT NULL
DROP TABLE dbo.IDs;

CREATE TABLE IDs
(
ID tinyint NOT NULL
)

INSERT INTO IDs
SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7;

IF OBJECT_ID('TestStats') IS NOT NULL
DROP TABLE dbo.TestStats;

CREATE TABLE dbo.TestStats
(
 ID tinyint NOT NULL,
 Col1 int NOT NULL,
 CONSTRAINT PK_TestStats PRIMARY KEY CLUSTERED (ID, col1)
);

DECLARE @id int = 1
DECLARE @i int = 1

WHILE @id <= 6
BEGIN
 SET @i = 1

WHILE @i <= 20247
BEGIN
    INSERT INTO dbo.TestStats VALUES(@id,@i);

    SET @i = @i + 1
END

SET @id = @id + 1
END

-- so far so good!
SELECT ID, COUNT(*) AS RowCnt FROM dbo.TestStats GROUP BY ID;

DBCC SHOW_STATISTICS('TestStats',PK_TestStats) WITH HISTOGRAM;
Run Code Online (Sandbox Code Playgroud)

现在我们有一个 ID 为 1 到 6 的表,每个 ID 有 20247 行。到目前为止,数据看起来不错!

第2步

-- now insert another ID = 7 with 20247 rows
DECLARE @i int = 1;

WHILE @i <= 20247
BEGIN
  INSERT INTO dbo.TestStats VALUES(7,@i);

  SET @i = @i + 1
END

-- see the problem with the histogram?
SELECT ID, COUNT(*) FROM dbo.TestStats GROUP BY ID;

DBCC SHOW_STATISTICS('TestStats',PK_TestStats) WITH HISTOGRAM;
Run Code Online (Sandbox Code Playgroud)

看看表格和直方图!实际表的 ID = 7 有 20247 行,但直方图不知道您刚刚插入了新数据,因为没有触发自动更新。根据公式,您需要插入 (20247 * 6) * 0.2 + 500 = 24,796.4 行以触发此表上统计信息的自动更新。

因此,如果您查看这些查询的计划,您会看到错误的估计:

-- CTRL + M to include the Actual Execution plan
-- now, IF we run these queries, the Optimizer has no info about ID = 7
-- and the Estimates 1 because it cannot say 0.
SELECT ts.*
FROM dbo.TestStats ts
INNER JOIN dbo.IDs ON IDs.ID = ts.ID
WHERE IDs.ID = 1;

SELECT ts.*
FROM dbo.TestStats ts
INNER JOIN dbo.IDs ON IDs.ID = ts.ID
WHERE IDs.ID = 7;
Run Code Online (Sandbox Code Playgroud)

查询#1:

查询#1:

查询#2:

查询#2

优化器不能说 0 行,所以它只显示 1。

步骤#3

-- now we manually update the stats
UPDATE STATISTICS dbo.TestStats WITH FULLSCAN;

-- check the histogram
DBCC SHOW_STATISTICS('TestStats',PK_TestStats) WITH HISTOGRAM;

-- rerun the queries
SELECT ts.*
FROM dbo.TestStats ts
INNER JOIN dbo.IDs ON IDs.ID = ts.ID
WHERE IDs.ID = 1;

SELECT ts.*
FROM dbo.TestStats ts
INNER JOIN dbo.IDs ON IDs.ID = ts.ID
WHERE IDs.ID = 7;
Run Code Online (Sandbox Code Playgroud)

现在直方图显示了缺失的 ID 7,执行计划也显示了正确的估计。

查询#1:

查询#1

查询#2:

查询#2

Q2) 随着 cacheId 数量的增加,您是否期望新插入数据的估计值自然提高?

是的,只要您从总行数中超过 20% + 500 的阈值。将触发自动更新。您可以通过重新运行 STEP#1 来运行此场景,然后通过运行以下查询来修改 STEP#2:

-- now insert another ID = 7 with 20247 rows
DECLARE @i int = 1;

WHILE @i <= 20247
BEGIN
   INSERT INTO dbo.TestStats VALUES(7,@i);

   SET @i = @i + 1
END

-- see the problem with the histogram?
SELECT ID, COUNT(*) FROM dbo.TestStats GROUP BY ID;

DBCC SHOW_STATISTICS('TestStats',PK_TestStats) WITH HISTOGRAM;
GO
-- try to insert ID = 8 to trigger the auto update for the stats
DECLARE @i int = 1;

WHILE @i <= 4548
BEGIN
  INSERT INTO dbo.TestStats VALUES(8,@i);

  SET @i = @i + 1
END

-- no update yet
SELECT ID, COUNT(*) FROM dbo.TestStats GROUP BY ID;

DBCC SHOW_STATISTICS('TestStats',PK_TestStats) WITH HISTOGRAM;
Run Code Online (Sandbox Code Playgroud)

还没有更新,因为阈值是 24,796.4 - 20247 = 4549.4 但我们只为 ID 8 插入了 4548 行。现在插入这一行并仔细检查直方图:

-- this will trigger the update
INSERT INTO dbo.TestStats VALUES(8,4549);

-- double check
SELECT ID, COUNT(*) FROM dbo.TestStats GROUP BY ID;

DBCC SHOW_STATISTICS('TestStats',PK_TestStats) WITH HISTOGRAM;
Run Code Online (Sandbox Code Playgroud)

Q3)是否有任何方法(吞咽,技巧或其他)来改进估计(或使其不太确定 1 行),而不必在每次插入新数据集时更新统计信息(例如在一个更大的 CacheId = 999999)。

控制 SQL Server 中的 Autostat (AUTO_UPDATE_STATISTICS) 行为

然而,当一个表变得非常大时,旧的阈值(一个固定的比率——20% 的行被改变)可能会太高,并且 Autostat 过程可能不会被足够频繁地触发。这可能会导致潜在的性能问题。SQL Server 2008 R2 Service Pack 1 和更高版本引入了跟踪标志 2371,您可以启用它来更改此默认行为。表中的行数越高,触发统计信息更新的阈值就越低。例如,如果跟踪标志被激活,则当发生 100 万次更改时,将对具有 10 亿行的表触发更新统计信息。如果未激活跟踪标志,那么在触发更新统计信息之前,具有 10 亿条记录的同一个表将需要进行 2 亿次更改。

希望这能帮助你理解!很好的问题!