Pojďme se podívat, co dělá query optimizer, když mu zabráníme používat statistiky.
Nejdříve si připravme tabulku pro testy a naplňme jí daty.
CREATE TABLE A(a INT, b CHAR(500)) GO SET NOCOUNT ON DECLARE @o INT = 1 DECLARE @p INT = 1 WHILE @o <= 50 BEGIN WHILE @p <= 200 BEGIN INSERT INTO A(a,b) VALUES(@p,'x') SET @p += 1 END SET @p = 1 SET @o += 1 END GO
Vytvoříme index, přičemž se nám vytvoří také statistika na základě všech hodnot ve sloupci a.
CREATE INDEX i_A ON A(a) GO
Nyní vyzkoušíme jak SQL server pracuje, když může statistiky využít. U následujících dotazů na základě statistik server přesně odhadne počet vrácených řádků, což si můžeme ověřit v exekučním plánu.
--odhaduje 50 řádků, vrací 50 řádků SELECT * FROM A WHERE a = 50 --odhaduje 500 řádků, vrací 500 řádků SELECT * FROM A WHERE a <= 10 --odhaduje 1000 řádků, vrací 1000 řádků SELECT * FROM A WHERE a <= 20 --odhaduje 3000 řádků, vrací 3000 řádků SELECT * FROM A WHERE a <= 60 --odhaduje 150 řádků, vrací 150 řádků SELECT * FROM A WHERE a BETWEEN 1 AND 3
Pokud však místo konstanty v podmínce výběru použijeme proměnné, pak server v době kompilace dotazu nezná jejích hodnotu a nemůže podle statistiky tedy odvodit počet vrácených řádků. Server v tomto případě počty vrácených řádků odhaduje na základě použitého operátoru v podmínce a to nezávisle na hodnotě proměnné.
--odhaduje 50 řádků, vrací 50 řádků DECLARE @a INT = 50 SELECT * FROM A WHERE a = @a GO --odhaduje 50 řádků, vrací 0 řádků DECLARE @a INT = 500 SELECT * FROM A WHERE a = @a GO --odhaduje 3000 řádků, vrací 500 řádků DECLARE @a INT = 10 SELECT * FROM A WHERE a <= @a GO --odhaduje 3000 řádků, vrací 1000 řádků DECLARE @a INT = 20 SELECT * FROM A WHERE a <= @a GO --odhaduje 3000 řádků, vrací 3000 řádků DECLARE @a INT = 60 SELECT * FROM A WHERE a <= @a GO --odhaduje 3000 řádků, vrací 0 řádků DECLARE @a INT = 200 SELECT * FROM A WHERE a > @a GO --odhaduje 900 řádků, vrací 150 řádků DECLARE @a INT = 1 DECLARE @b INT = 3 SELECT * FROM A WHERE a BETWEEN @a AND @b
Pojďme si vysvětlit jak k takovým odhadům server dojde. Sloupec a obsahuje 10 000 hodnot a 200 jedinečných hodnot. Statistika i_A obsahuje hodnotu All density, která se vypočte jako 1 / počet jedinečných hodnot, tedy v našem případě 1 / 200 = 0.005.
Pro operátor rovnosti server odhaduje počet vrácených řádků jako All density * počet řádků. V našem případě 0.005 * 10 000 = 50.
Pro operátory >, <, >=, <= odhaduje počet vrácených řádků jako 30% z celkového počtu řádků, tedy 0.3 * 10 000 = 3000.
Pro operátor BETWEEN odhaduje server počet vrácených řádků jako 9% z celkového počtu řádků, tedy 0.09 * 10 000 = 900.
Přesně takto kvalitní odhady server používá, pokud vnější proměnné z procedury předáme do vnitřních proměnných a snažíme se tak zamezit parametr sniffingu.