Voici un livre blanc sur le moment où la mise à jour automatique des statistiques se produit . Voici les points saillants des mises à jour automatiques des statistiques:
- La taille du tableau est passée de 0 à> 0 lignes (test 1).
- Le nombre de lignes dans le tableau lors de la collecte des statistiques était de 500 ou moins, et le colmodctr de la première colonne de l'objet de statistiques a changé de plus de 500 depuis lors (test 2).
- Le tableau comportait plus de 500 lignes lors de la collecte des statistiques et le colmodctr de la colonne de tête de l'objet statistique a changé de plus de 500 + 20% du nombre de lignes du tableau lors de la collecte des statistiques (test 3) .
@JNK a donc fait remarquer dans un commentaire que si vous avez 1 milliard de lignes dans une table, vous auriez besoin d'avoir 20 000 500 000 écritures dans la première colonne de la statistique pour déclencher une mise à jour.
Prenons la structure suivante:
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);
Maintenant, nous pouvons vérifier pour voir ce qui s'est passé dans les statistiques.
select *
from sys.stats
where object_id = OBJECT_ID('dbo.test_table')
Cependant, pour voir s'il s'agit d'un objet statistique significatif, nous devons:
dbcc show_statistics('dbo.test_table',cix_test_table)
Cette statistique n'a donc pas été mise à jour. En effet, il semble que la statistique ne soit pas mise à jour jusqu'à ce qu'un SELECT
se produise et même alors, le SELECT
doit être en dehors de ce que SQL Server a dans son histogramme. Voici un script de test que j'ai exécuté pour tester ceci:
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
Au lieu de désactiver aveuglément les statistiques auto_update, j'essayerais d'examiner votre ensemble de données pour le biais. Si vos données présentent un biais important, vous devez envisager de créer des statistiques filtrées, puis décider si la gestion manuelle des mises à jour des statistiques est la bonne solution.
Pour analyser le biais, vous devez exécuter DBCC SHOW_STATISTICS(<stat_object>, <index_name>);
(dans le script ci-dessus sans le WITH STAT_HEADER
) sur la combinaison stat / index particulière que vous souhaitez examiner. Un moyen rapide de surveiller votre asymétrie serait de regarder l'histogramme (troisième jeu de résultats) et de vérifier la variance de votre EQ_ROWS
. Si c'est assez cohérent, votre asymétrie est minime. Pour l'intensifier, vous regardez la RANGE_ROWS
colonne et regardez la variance car elle mesure le nombre de lignes entre chaque étape. Enfin, vous pouvez prendre le [All density]
résultat du DENSITY_VECTOR
(deuxième jeu de résultats) et le multiplier par la [Rows Sampled]
valeur du STAT_HEADER
(premier jeu de résultats) et voir quelle serait l'attente moyenne pour une requête sur cette colonne. Vous comparez cette moyenne à votreEQ_ROWS
et s'il y a beaucoup d'endroits où cela varie considérablement, alors vous avez un biais.
Si vous constatez que vous avez une asymétrie, vous devez envisager de créer des statistiques filtrées sur les plages qui sont élevées très élevées RANGE_ROWS
afin de pouvoir donner des étapes supplémentaires pour de meilleures estimations sur ces valeurs.
Une fois ces statistiques filtrées en place, vous pouvez envisager la possibilité de mettre à jour manuellement les statistiques.