这是关于何时自动更新统计信息的白皮书。以下是自动更新统计信息的要点:
- 表的大小从0行增加到0行以上(测试1)。
- 收集统计信息时表中的行数为500或更少,并且此后统计信息对象的前列的colmodctr更改了500多个(测试2)。
- 收集统计信息时,该表具有超过500行,并且收集统计信息时,统计对象的前导列的colmodctr更改了表中的行数的500 + 20%以上(测试3) 。
因此,@ JNK在注释中指出,如果表中有10亿行,则需要对统计信息的第一列进行20,000,5000次写操作才能触发更新。
让我们采用以下结构:
CREATE TABLE dbo.test_table (
test_table_id INTEGER IDENTITY(1,1) NOT NULL,
test_table_value VARCHAR(50),
test_table_value2 BIGINT,
test_table_value3 NUMERIC(10,2)
);
CREATE CLUSTERED INDEX cix_test_table ON dbo.test_table (test_table_id, test_table_value);
现在我们可以检查一下统计领域发生了什么。
select *
from sys.stats
where object_id = OBJECT_ID('dbo.test_table')
但是,要查看这是否是有意义的统计对象,我们需要:
dbcc show_statistics('dbo.test_table',cix_test_table)
因此,该统计信息尚未更新。那是因为看起来统计数据直到a SELECT
出现才更新,即使如此,它SELECT
也必须超出SQL Server在其直方图中的范围。这是我用来测试的测试脚本:
CREATE TABLE test_table (
test_table_id INTEGER IDENTITY(1,1) NOT NULL,
test_table_value VARCHAR(50),
test_table_value2 BIGINT,
test_table_value3 NUMERIC(10,2)
);
CREATE CLUSTERED INDEX cix_test_table ON test_table (test_table_id, test_table_value);
ALTER TABLE test_table ADD CONSTRAINT pk_test_table PRIMARY KEY (test_table_id)
SELECT *
FROM sys.stats
WHERE object_id = OBJECT_ID('dbo.test_table')
--DBCC SHOW_STATISTICS('dbo.test_table',pk_test_table)
DBCC SHOW_STATISTICS('dbo.test_table',cix_test_table) WITH STAT_HEADER;
declare @test int = 0
WHILE @test < 1
BEGIN
INSERT INTO test_table (test_table_value,test_table_value2,test_table_value3) VALUES
('stats test' + CAST(@test AS VARCHAR(10)),@test, @test)
SET @test = @test + 1;
END
SELECT 'one row|select < 1', * FROM test_table WHERE test_table_id < 1;
--DBCC SHOW_STATISTICS('dbo.test_table',pk_test_table);
DBCC SHOW_STATISTICS('dbo.test_table',cix_test_table) WITH STAT_HEADER;
SET @test = 1
WHILE @test < 500
BEGIN
INSERT INTO test_table (test_table_value,test_table_value2,test_table_value3) VALUES
('stats test' + CAST(@test AS VARCHAR(10)),@test, @test)
SET @test = @test + 1;
END
SELECT '100 rows(add 99)|select < 100',* FROM test_table WHERE test_table_id < 100;
--DBCC SHOW_STATISTICS('dbo.test_table',pk_test_table);
DBCC SHOW_STATISTICS('dbo.test_table',cix_test_table) WITH STAT_HEADER;
--get the table up to 500 rows/changes
WHILE @test < 500
BEGIN
INSERT INTO test_table (test_table_value,test_table_value2,test_table_value3) VALUES
('stats test' + CAST(@test AS VARCHAR(10)),@test, @test)
SET @test = @test + 1;
END
SELECT '500 rows(add 400)|select < 100',* FROM test_table WHERE test_table_id < 100;
DBCC SHOW_STATISTICS('dbo.test_table',cix_test_table) WITH STAT_HEADER;
SELECT '500 rows(add 400)|select < 500',* FROM test_table WHERE test_table_id < 500;
--DBCC SHOW_STATISTICS('dbo.test_table',pk_test_table);
DBCC SHOW_STATISTICS('dbo.test_table',cix_test_table) WITH STAT_HEADER;
--bump it to 501
SET @test = 500;
WHILE @test < 501
BEGIN
INSERT INTO test_table (test_table_value,test_table_value2,test_table_value3) VALUES
('stats test' + CAST(@test AS VARCHAR(10)),@test, @test)
SET @test = @test + 1;
END
SELECT '501 rows(add 1)|select < 501',* FROM test_table WHERE test_table_id < 501;
--DBCC SHOW_STATISTICS('dbo.test_table',pk_test_table);
DBCC SHOW_STATISTICS('dbo.test_table',cix_test_table) WITH STAT_HEADER;
--bump it to 600
SET @test = 501;
WHILE @test < 600
BEGIN
INSERT INTO test_table (test_table_value,test_table_value2,test_table_value3) VALUES
('stats test' + CAST(@test AS VARCHAR(10)),@test, @test)
SET @test = @test + 1;
END
SELECT '600 rows (add 100)|select < 600',* FROM test_table WHERE test_table_id < 600;
--DBCC SHOW_STATISTICS('dbo.test_table',pk_test_table);
DBCC SHOW_STATISTICS('dbo.test_table',cix_test_table) WITH STAT_HEADER;
--bump it to 700
SET @test = 600;
WHILE @test < 700
BEGIN
INSERT INTO test_table (test_table_value,test_table_value2,test_table_value3) VALUES
('stats test' + CAST(@test AS VARCHAR(10)),@test, @test)
SET @test = @test + 1;
END
SELECT '700 rows (add 100)|select < 700', * FROM test_table WHERE test_table_id < 700;
--DBCC SHOW_STATISTICS('dbo.test_table',pk_test_table);
DBCC SHOW_STATISTICS('dbo.test_table',cix_test_table) WITH STAT_HEADER;
--bump it to 1200
SET @test = 700;
WHILE @test < 1200
BEGIN
INSERT INTO test_table (test_table_value,test_table_value2,test_table_value3) VALUES
('stats test' + CAST(@test AS VARCHAR(10)),@test, @test)
SET @test = @test + 1;
END
SELECT '1200 rows (add 500)|select < 1200',* FROM test_table WHERE test_table_id < 1200;
--DBCC SHOW_STATISTICS('dbo.test_table',pk_test_table);
DBCC SHOW_STATISTICS('dbo.test_table',cix_test_table) WITH STAT_HEADER;
--DROP TABLE test_table
与其盲目地禁用auto_update统计信息,不如尝试检查您的数据集是否倾斜。如果数据表现出明显的偏差,那么您需要考虑创建过滤的统计信息,然后决定手动管理统计信息更新是否是正确的做法。
要分析偏斜,您需要DBCC SHOW_STATISTICS(<stat_object>, <index_name>);
在WITH STAT_HEADER
要检查的特定统计信息/索引组合上运行(在上面的脚本中,没有)。观察偏斜的一种快速方法是查看直方图(第三结果集),并检查中的方差EQ_ROWS
。如果相当一致,则您的歪斜很小。要逐步提高它,您需要查看该RANGE_ROWS
列并查看其中的差异,因为这可以衡量每个步骤之间存在多少行。最后,您可以[All density]
从DENSITY_VECTOR
(第二个结果集)中获取结果,并将其乘以(第一个结果集)中的[Rows Sampled]
值,STAT_HEADER
然后查看对该列查询的平均期望值。您将该平均值与您的平均值进行比较EQ_ROWS
如果在很多地方变化很大,那您就会有偏差。
如果发现确实存在偏斜,则需要考虑在具有很高或很高的范围上创建一些过滤的统计信息,RANGE_ROWS
以便您可以给出其他步骤以更好地估计这些值。
一旦有了这些过滤的统计信息,便可以查看手动更新统计信息的可能性。