sas*_*aso 12 data-warehouse sql-server statistics
我在 SQL Server 中有 200 GB 的数据仓库。
对于某些查询,我遇到了非常缓慢的执行时间;例如delete
,使用inner join
.
在对执行计划进行了一些研究之后,我使用该WITH FULLSCAN
选项更新了查询中涉及的 2 个表的统计信息。
查询现在在不到一秒的时间内执行,因此统计信息似乎不是最新的。
我正在考虑禁用auto update statistics
数据库并UPDATE STATISTICS
在加载数据仓库后手动运行。每天晚上从源 ERP 系统增量加载数据仓库。
我假设auto update statistics
在数据仓库场景中不是真的有用吗?相反,在数据加载后手动更新统计数据是否更有意义?
swa*_*eck 11
这是有关何时发生统计数据的 auto_update 的白皮书。以下是与统计数据的自动更新相比的要点:
- 表大小已从 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);
Run Code Online (Sandbox Code Playgroud)
现在我们可以检查一下统计领域发生了什么。
select *
from sys.stats
where object_id = OBJECT_ID('dbo.test_table')
Run Code Online (Sandbox Code Playgroud)
但是,要查看这是否是一个有意义的统计对象,我们需要:
dbcc show_statistics('dbo.test_table',cix_test_table)
Run Code Online (Sandbox Code Playgroud)
所以这个统计数据还没有更新。那是因为看起来统计数据在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
Run Code Online (Sandbox Code Playgroud)
我不会盲目地禁用 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
如果有很多地方变化很大,那么你就有了偏差。
如果您发现确实存在偏差,那么您需要考虑在具有 high 非常 high 的范围上创建一些过滤统计,RANGE_ROWS
以便您可以提供额外的步骤来更好地估计这些值。
一旦您准备好这些过滤的统计信息,您就可以查看手动更新统计信息的可能性。