Як визначається кількість етапів гістограми в статистиці


11

Як визначається кількість етапів гістограми в статистиці в SQL Server?

Чому він обмежений 200 кроками, навіть якщо мій стовпець клавіш містить більше 200 чітких значень? Чи є вирішальний фактор?


Демо

Визначення схеми

CREATE TABLE histogram_step
  (
     id   INT IDENTITY(1, 1),
     name VARCHAR(50),
     CONSTRAINT pk_histogram_step PRIMARY KEY (id)
  )

Вставлення 100 записів у мою таблицю

INSERT INTO histogram_step
            (name)
SELECT TOP 100 name
FROM   sys.syscolumns

Оновлення та перевірка статистики

UPDATE STATISTICS histogram_step WITH fullscan

DBCC show_statistics('histogram_step', pk_histogram_step)

Етапи гістограми:

+--------------+------------+---------+---------------------+----------------+
| RANGE_HI_KEY | RANGE_ROWS | EQ_ROWS | DISTINCT_RANGE_ROWS | AVG_RANGE_ROWS |
+--------------+------------+---------+---------------------+----------------+
|            1 |          0 |       1 |                   0 |              1 |
|            3 |          1 |       1 |                   1 |              1 |
|            5 |          1 |       1 |                   1 |              1 |
|            7 |          1 |       1 |                   1 |              1 |
|            9 |          1 |       1 |                   1 |              1 |
|           11 |          1 |       1 |                   1 |              1 |
|           13 |          1 |       1 |                   1 |              1 |
|           15 |          1 |       1 |                   1 |              1 |
|           17 |          1 |       1 |                   1 |              1 |
|           19 |          1 |       1 |                   1 |              1 |
|           21 |          1 |       1 |                   1 |              1 |
|           23 |          1 |       1 |                   1 |              1 |
|           25 |          1 |       1 |                   1 |              1 |
|           27 |          1 |       1 |                   1 |              1 |
|           29 |          1 |       1 |                   1 |              1 |
|           31 |          1 |       1 |                   1 |              1 |
|           33 |          1 |       1 |                   1 |              1 |
|           35 |          1 |       1 |                   1 |              1 |
|           37 |          1 |       1 |                   1 |              1 |
|           39 |          1 |       1 |                   1 |              1 |
|           41 |          1 |       1 |                   1 |              1 |
|           43 |          1 |       1 |                   1 |              1 |
|           45 |          1 |       1 |                   1 |              1 |
|           47 |          1 |       1 |                   1 |              1 |
|           49 |          1 |       1 |                   1 |              1 |
|           51 |          1 |       1 |                   1 |              1 |
|           53 |          1 |       1 |                   1 |              1 |
|           55 |          1 |       1 |                   1 |              1 |
|           57 |          1 |       1 |                   1 |              1 |
|           59 |          1 |       1 |                   1 |              1 |
|           61 |          1 |       1 |                   1 |              1 |
|           63 |          1 |       1 |                   1 |              1 |
|           65 |          1 |       1 |                   1 |              1 |
|           67 |          1 |       1 |                   1 |              1 |
|           69 |          1 |       1 |                   1 |              1 |
|           71 |          1 |       1 |                   1 |              1 |
|           73 |          1 |       1 |                   1 |              1 |
|           75 |          1 |       1 |                   1 |              1 |
|           77 |          1 |       1 |                   1 |              1 |
|           79 |          1 |       1 |                   1 |              1 |
|           81 |          1 |       1 |                   1 |              1 |
|           83 |          1 |       1 |                   1 |              1 |
|           85 |          1 |       1 |                   1 |              1 |
|           87 |          1 |       1 |                   1 |              1 |
|           89 |          1 |       1 |                   1 |              1 |
|           91 |          1 |       1 |                   1 |              1 |
|           93 |          1 |       1 |                   1 |              1 |
|           95 |          1 |       1 |                   1 |              1 |
|           97 |          1 |       1 |                   1 |              1 |
|           99 |          1 |       1 |                   1 |              1 |
|          100 |          0 |       1 |                   0 |              1 |
+--------------+------------+---------+---------------------+----------------+

Як ми бачимо, у гістограмі є 53 кроки.

Знову вставляємо кілька тисяч записів

INSERT INTO histogram_step
            (name)
SELECT TOP 10000 b.name
FROM   sys.syscolumns a
       CROSS JOIN sys.syscolumns b

Оновлення та перевірка статистики

UPDATE STATISTICS histogram_step WITH fullscan

DBCC show_statistics('histogram_step', pk_histogram_step)

Тепер етапи гістограми зменшуються до 4 етапів

+--------------+------------+---------+---------------------+----------------+
| RANGE_HI_KEY | RANGE_ROWS | EQ_ROWS | DISTINCT_RANGE_ROWS | AVG_RANGE_ROWS |
+--------------+------------+---------+---------------------+----------------+
|            1 |          0 |       1 |                   0 |              1 |
|        10088 |      10086 |       1 |               10086 |              1 |
|        10099 |         10 |       1 |                  10 |              1 |
|        10100 |          0 |       1 |                   0 |              1 |
+--------------+------------+---------+---------------------+----------------+

Знову вставляємо кілька тисяч записів

INSERT INTO histogram_step
            (name)
SELECT TOP 100000 b.name
FROM   sys.syscolumns a
       CROSS JOIN sys.syscolumns b

Оновлення та перевірка статистики

UPDATE STATISTICS histogram_step WITH fullscan

DBCC show_statistics('histogram_step', pk_histogram_step) 

Тепер етапи гістограми зводяться до 3 етапів

+--------------+------------+---------+---------------------+----------------+
| RANGE_HI_KEY | RANGE_ROWS | EQ_ROWS | DISTINCT_RANGE_ROWS | AVG_RANGE_ROWS |
+--------------+------------+---------+---------------------+----------------+
|            1 |          0 |       1 |                   0 |              1 |
|       110099 |     110097 |       1 |              110097 |              1 |
|       110100 |          0 |       1 |                   0 |              1 |
+--------------+------------+---------+---------------------+----------------+

Може хтось скаже мені, як вирішуються ці кроки?


3
200 було довільним вибором. Це не має нічого спільного з тим, скільки різних значень у певній таблиці. Якщо ви хочете знати, чому було обрано 200, вам доведеться запитати інженера з команди SQL Server 1990-х років, а не ваших колег
Аарона Бертран

1
@AaronBertrand - Дякую .. Тож як вирішено ці кроки
P ரதீப்

1
Рішення немає. Верхня межа - 200. Період. Ну, технічно, це 201 рік, але це історія для іншого дня.
Аарон Бертран

1
Я задавав подібне запитання про оцінки кроків
jesijesi

Відповіді:


14

Я збираюся обмежити цю публікацію обговоренням статистики одного стовпця, тому що вона вже буде досить тривалою, і ви зацікавлені в тому, як SQL Server з'єднує дані в кроки гістограми. Для статистики декількох стовпців гістограма створюється лише на провідному стовпці.

Коли SQL Server визначає, що необхідне оновлення статистики, він починає прихований запит, який читає або всі дані таблиці, або зразок даних таблиці. Ви можете переглядати ці запити з розширеними подіями. Існує функція, яка називається StatManв SQL Server, яка бере участь у створенні гістограм. Для простих об'єктів статистики існує щонайменше два різних типи StatManзапитів (є різні запити для швидкого оновлення статистики, і я підозрюю, що функція додаткової статистики в розділених таблицях також використовує інший запит).

Перший просто збирає всі дані з таблиці без будь-якої фільтрації. Це ви можете бачити, коли таблиця дуже мала або ви збираєте статистику з FULLSCANопцією:

CREATE TABLE X_SHOW_ME_STATMAN (N INT);
CREATE STATISTICS X_STAT_X_SHOW_ME_STATMAN ON X_SHOW_ME_STATMAN (N);

-- after gathering stats with 1 row in table
SELECT StatMan([SC0]) FROM
(
    SELECT TOP 100 PERCENT [N] AS [SC0] 
    FROM [dbo].[X_SHOW_ME_STATMAN] WITH (READUNCOMMITTED)
    ORDER BY [SC0] 
) AS _MS_UPDSTATS_TBL 
OPTION (MAXDOP 16);

SQL Server вибирає автоматичний розмір вибірки залежно від розміру таблиці (я думаю, що це і кількість рядків, і сторінок у таблиці). Якщо таблиця занадто велика, то розмір автоматичного зразка падає нижче 100%. Ось що я отримав для тієї ж таблиці з 1М рядками:

-- after gathering stats with 1 M rows in table
SELECT StatMan([SC0], [SB0000]) FROM 
(
    SELECT TOP 100 PERCENT [SC0], step_direction([SC0]) over (order by NULL) AS [SB0000] 
    FROM 
    (
        SELECT [N] AS [SC0] 
        FROM [dbo].[X_SHOW_ME_STATMAN] TABLESAMPLE SYSTEM (6.666667e+001 PERCENT) WITH (READUNCOMMITTED) 
    ) AS _MS_UPDSTATS_TBL_HELPER 
    ORDER BY [SC0], [SB0000] 
) AS _MS_UPDSTATS_TBL
OPTION (MAXDOP 1);

TABLESAMPLEє документально підтвердженим, але StatMan та step_direction - ні. тут SQL Server відбирає близько 66,6% даних із таблиці для створення гістограми. Це означає, що ви могли отримати різну кількість кроків гістограми, оновлюючи статистику (без FULLSCAN) на одних і тих же даних. Я ніколи цього не спостерігав на практиці, але не бачу, чому це було б неможливо.

Давайте проведемо кілька тестів на простих даних, щоб побачити, як статистика змінюється з часом. Нижче наведено тестовий код, який я написав, щоб вставити послідовні цілі числа в таблицю, зібрати статистику після кожного вставки та зберегти інформацію про статистику в таблицю результатів. Почнемо з просто вставлення 1 рядки до 10000. Тестовий шар:

DECLARE
@stats_id INT,
@table_object_id INT,
@rows_per_loop INT = 1,
@num_of_loops INT = 10000,
@loop_num INT;

BEGIN
    SET NOCOUNT ON;

    TRUNCATE TABLE X_STATS_RESULTS;

    SET @table_object_id = OBJECT_ID ('X_SEQ_NUM');
    SELECT @stats_id = stats_id FROM sys.stats
    WHERE OBJECT_ID = @table_object_id
    AND name = 'X_STATS_SEQ_INT_FULL';

    SET @loop_num = 0;
    WHILE @loop_num < @num_of_loops
    BEGIN
        SET @loop_num = @loop_num + 1;

        INSERT INTO X_SEQ_NUM WITH (TABLOCK)
        SELECT @rows_per_loop * (@loop_num - 1) + N FROM dbo.GetNums(@rows_per_loop);

        UPDATE STATISTICS X_SEQ_NUM X_STATS_SEQ_INT_FULL WITH FULLSCAN; -- can comment out FULLSCAN as needed

        INSERT INTO X_STATS_RESULTS WITH (TABLOCK)
        SELECT 'X_STATS_SEQ_INT_FULL', @rows_per_loop * @loop_num, rows_sampled, steps 
        FROM sys.dm_db_stats_properties(@table_object_id, @stats_id);
        END;
END;

За цими даними кількість кроків гістограми швидко збільшується до 200 (спочатку вона вражає максимальну кількість кроків з 397 рядків), залишається на рівні 199 або 200, поки 1485 рядків не знаходяться в таблиці, потім повільно зменшується, поки гістограма не набере лише 3 або 4 кроки. Ось графік усіх даних:

перший графік

Ось гістограма виглядає як для 10-ти рядків:

RANGE_HI_KEY    RANGE_ROWS  EQ_ROWS DISTINCT_RANGE_ROWS AVG_RANGE_ROWS
1               0           1       0                   1
9999            9997        1       9997                1
10000           0           1       0                   1

Чи проблема в тому, що гістограма має лише 3 кроки? Схоже, інформація збереглася з нашої точки зору. Зауважте, що оскільки тип даних є INTEGER, ми можемо визначити, скільки рядків знаходиться в таблиці для кожного цілого числа від 1 до 10000. Зазвичай SQL Server може і це вирішити, хоча є деякі випадки, коли це не зовсім виходить. . Дивіться цей приклад SE для прикладу цього.

Як ви думаєте, що станеться, якщо ми видалимо з таблиці один рядок та оновимо статистику? В ідеалі ми отримаємо ще один крок гістограми, щоб показати, що відсутнє ціле число більше не знаходиться в таблиці.

DELETE FROM X_SEQ_NUM
WHERE X_NUM  = 1000;

UPDATE STATISTICS X_SEQ_NUM X_STATS_SEQ_INT_FULL WITH FULLSCAN;

DBCC SHOW_STATISTICS ('X_SEQ_NUM', 'X_STATS_SEQ_INT_FULL'); -- still 3 steps

DELETE FROM X_SEQ_NUM
WHERE X_NUM  IN (2000, 3000, 4000, 5000, 6000, 7000, 8000, 9000);

UPDATE STATISTICS X_SEQ_NUM X_STATS_SEQ_INT_FULL WITH FULLSCAN;

DBCC SHOW_STATISTICS ('X_SEQ_NUM', 'X_STATS_SEQ_INT_FULL'); -- still 3 steps

Це трохи розчаровує. Якби ми будували гістограму вручну, ми додавали б крок для кожного відсутнього значення. SQL Server використовує алгоритм загального призначення, тому для деяких наборів даних ми можемо придумати більш підходящу гістограму, ніж код, який він використовує. Звичайно, практична різниця між отриманням 0 або 1 ряду з таблиці дуже мала. Я отримую однакові результати при тестуванні з 20000 рядків, кожне ціле число яких має 2 рядки в таблиці. Після видалення даних гістограма не отримує кроків.

RANGE_HI_KEY    RANGE_ROWS  EQ_ROWS DISTINCT_RANGE_ROWS AVG_RANGE_ROWS
1               0           2       0                   1
9999            19994       2       9997                2
10000           0           2       0                   1

Якщо я тестую 1 мільйон рядків, кожне ціле число яких має 100 рядків у таблиці, я отримую трохи кращі результати, але все одно можу скласти кращу гістограму вручну.

truncate table X_SEQ_NUM;

BEGIN TRANSACTION;
INSERT INTO X_SEQ_NUM WITH (TABLOCK)
SELECT N FROM dbo.GetNums(10000);
GO 100
COMMIT TRANSACTION;

UPDATE STATISTICS X_SEQ_NUM X_STATS_SEQ_INT_FULL WITH FULLSCAN;

DBCC SHOW_STATISTICS ('X_SEQ_NUM', 'X_STATS_SEQ_INT_FULL'); -- 4 steps

DELETE FROM X_SEQ_NUM
WHERE X_NUM  = 1000;

UPDATE STATISTICS X_SEQ_NUM X_STATS_SEQ_INT_FULL WITH FULLSCAN;

DBCC SHOW_STATISTICS ('X_SEQ_NUM', 'X_STATS_SEQ_INT_FULL'); -- now 5 steps with a RANGE_HI_KEY of 998 (?)

DELETE FROM X_SEQ_NUM
WHERE X_NUM  IN (2000, 3000, 4000, 5000, 6000, 7000, 8000, 9000);

UPDATE STATISTICS X_SEQ_NUM X_STATS_SEQ_INT_FULL WITH FULLSCAN;

DBCC SHOW_STATISTICS ('X_SEQ_NUM', 'X_STATS_SEQ_INT_FULL'); -- still 5 steps

Підсумкова гістограма:

RANGE_HI_KEY    RANGE_ROWS  EQ_ROWS DISTINCT_RANGE_ROWS AVG_RANGE_ROWS
1               0           100     0                   1
998             99600       100     996                 100
3983            298100      100     2981                100
9999            600900      100     6009                100
10000           0           100     0                   1

Давайте перевіримо далі з послідовними цілими числами, але з більшою кількістю рядків у таблиці. Зауважте, що для таблиць, які є занадто маленькими вручну, вказуючи розмір вибірки, це не матиме ефекту, тому я додаватиму 100 рядків у кожну вставку та збиратиму статистику щоразу до 1 мільйона рядків. Я бачу аналогічну схему, як і раніше, за винятком того, як раз я потрапляю до 637300 рядків у таблиці, я більше не відбираю 100% рядків у таблиці зі стандартною частотою вибірки. У міру отримання рядків кількість кроків гістограми збільшується. Можливо, це тому, що SQL Server закінчується більшою кількістю прогалин у даних, оскільки кількість непробованих рядків у таблиці збільшується. Я не забиваю 200 кроків навіть на 1 М рядках, але якби я продовжував додавати рядки, я сподіваюся, що дістанусь там і врешті-решт почну йти назад.

графік 2

Вісь X - це кількість рядків у таблиці. Зі збільшенням кількості рядків вибіркові рядки дещо змінюються і не перевищують 650 к.

Тепер давайте зробимо кілька простих тестів з даними VARCHAR.

CREATE TABLE X_SEQ_STR (X_STR VARCHAR(5));
CREATE STATISTICS X_SEQ_STR ON X_SEQ_STR(X_STR);

Тут я вставляю 200 номерів (як рядки) разом із NULL.

INSERT INTO X_SEQ_STR
SELECT N FROM dbo.GetNums(200)
UNION ALL
SELECT NULL;

UPDATE STATISTICS X_SEQ_STR X_SEQ_STR ;

DBCC SHOW_STATISTICS ('X_SEQ_STR', 'X_SEQ_STR'); -- 111 steps, RANGE_ROWS is 0 or 1 for all steps

Зауважте, що NULL завжди отримує власний крок гістограми, коли він знайдений у таблиці. SQL Server міг би дати мені рівно 201 крок, щоб зберегти всю інформацію, але цього не зробили. Технічно інформація втрачається, оскільки "1111" сортує між "1" і "2", наприклад.

Тепер спробуємо вставити різні символи, а не просто цілі числа:

truncate table X_SEQ_STR;

INSERT INTO X_SEQ_STR
SELECT CHAR(10 + N) FROM dbo.GetNums(200)
UNION ALL
SELECT NULL;

UPDATE STATISTICS X_SEQ_STR X_SEQ_STR ;

DBCC SHOW_STATISTICS ('X_SEQ_STR', 'X_SEQ_STR'); -- 95 steps, RANGE_ROWS is 0 or 1 or 2

Ніякої реальної різниці від останнього тесту.

Тепер спробуємо вставити символи, але покладемо різні таблиці кожного символу в таблицю. Наприклад, CHAR(11)має 1 ряд, CHAR(12)має 2 ряди тощо.

truncate table X_SEQ_STR;

DECLARE
@loop_num INT;

BEGIN
    SET NOCOUNT ON;

    SET @loop_num = 0;
    WHILE @loop_num < 200
    BEGIN
        SET @loop_num = @loop_num + 1;

        INSERT INTO X_SEQ_STR WITH (TABLOCK)
        SELECT CHAR(10 + @loop_num) FROM dbo.GetNums(@loop_num);
    END;
END;

UPDATE STATISTICS X_SEQ_STR X_SEQ_STR ;

DBCC SHOW_STATISTICS ('X_SEQ_STR', 'X_SEQ_STR'); -- 148 steps, most with RANGE_ROWS of 0

Як і раніше, я все ще не отримую рівно 200 кроків гістограми. Однак багато кроків мають RANGE_ROWS0.

Для остаточного тесту я збираюся вставити випадковий рядок з 5 символів у кожен цикл і збирати статистику кожного разу. Ось код випадкового рядка:

char((rand()*25 + 65))+char((rand()*25 + 65))+char((rand()*25 + 65))+char((rand()*25 + 65))+char((rand()*25 + 65))

Ось графік рядків у кроках таблиці та гістограми: графік 3

Зауважте, що кількість кроків не опускається нижче 100, коли вона починає підніматися вгору і вниз. Я десь чув (але наразі не можу отримати джерело), ​​що алгоритм побудови гістограми SQL Server поєднує кроки гістограми, оскільки у них не вистачає місця. Таким чином, ви можете закінчити різкі зміни кількості кроків, лише додавши трохи даних. Ось один зразок даних, який мені здався цікавим:

ROWS_IN_TABLE   ROWS_SAMPLED    STEPS
36661           36661           133
36662           36662           143
36663           36663           143
36664           36664           141
36665           36665           138

Навіть під час вибірки з FULLSCANдодаванням одного рядка можна збільшити кількість кроків на 10, тримати його постійним, потім зменшити на 2, потім зменшити на 3.

Що ми можемо підсумувати з усього цього? Я нічого не можу довести, але ці спостереження, мабуть, справджуються:

  • SQL Server використовує алгоритм загального використання для створення гістограм. Для деяких розподілів даних можливо створити більш повне представлення даних вручну.
  • Якщо в таблиці є дані NULL, а запит статистики виявляє, що дані NULL завжди отримують власний крок гістограми.
  • Мінімальне значення, знайдене в таблиці, отримує власний крок гістограми з RANGE_ROWS= 0.
  • Максимальне значення, знайдене в таблиці, буде остаточним RANGE_HI_KEYу таблиці.
  • Оскільки SQL Server відбирає більше даних, можливо, буде потрібно комбінувати існуючі кроки, щоб звільнити місця для нових даних, які вони знаходять. Якщо ви подивитесь на достатню кількість гістограм, ви можете побачити загальні значення, які повторюються для DISTINCT_RANGE_ROWSабо RANGE_ROWS. Наприклад, 255 показує купу разів для RANGE_ROWSі DISTINCT_RANGE_ROWSдля остаточного тестового випадку тут.
  • Для простого розподілу даних ви можете бачити, як SQL Server поєднує послідовні дані в один етап гістограми, що не призводить до втрати інформації. Однак при додаванні прогалин до даних гістограма може не коригуватись так, як ви б сподівалися.

Коли все це проблема? Це проблема, коли запит працює погано через гістограму, яка не в змозі представити розподіл даних таким чином, щоб оптимізатор запитів прийняв хороші рішення. Я думаю, що є тенденція думати, що робити більше кроків гістограми - це завжди краще, і це може бути дивовижною ситуацією, коли SQL Server генерує гістограму на мільйони рядків і більше, але не використовує точно 200 або 201 кроків гістограми. Однак я бачив багато проблем зі статистикою, навіть коли гістограма має 200 або 201 кроки. Ми не маємо ніякого контролю над тим, скільки кроків гістограми генерує SQL Server для об’єкта статистики, тому я б не турбувався про це. Однак є кілька кроків, які ви можете зробити, коли у вас виникають погані результати запитів, викликані проблемами статистики. Я дам надзвичайно короткий огляд.

Збір статистики повністю може допомогти в деяких випадках. Для дуже великих таблиць розмір автоматичної вибірки може бути менше 1% рядків у таблиці. Іноді це може призвести до поганих планів залежно від порушення даних у стовпці. Документація Microsofts для CREATE STATISTICS та UPDATE STATISTICS говорить стільки:

Зразок корисний для особливих випадків, коли план запитів на основі вибірки за замовчуванням не є оптимальним. У більшості ситуацій не потрібно вказувати SAMPLE, оскільки оптимізатор запитів вже використовує вибірку і визначає статистично значущий розмір вибірки за замовчуванням, як це потрібно для створення високоякісних планів запитів.

Для більшості робочих навантажень повне сканування не потрібно, а вибіркова за замовчуванням є достатньою. Однак певні робочі навантаження, чутливі до широко змінюваних розподілів даних, можуть вимагати збільшення розміру вибірки або навіть повного сканування.

У деяких випадках може допомогти створення відфільтрованої статистики. У вас може бути стовпець зі скошеними даними та безліччю різних відмінних значень. Якщо в даних, які зазвичай відфільтровані, є певні значення, ви можете створити гістограму статистики лише для тих загальних значень. Оптимізатор запитів може використовувати статистику, визначену на меншому діапазоні даних, а не статистику, визначену для всіх значень стовпців. Ви все одно не гарантовано отримаєте 200 кроків у гістограмі, але якщо ви створите відфільтровану статистику лише на одному значенні, ви зробите крок гістограми на це значення.

Використання розділеного виду - це один із способів ефективно отримати більше 200 кроків для таблиці. Припустимо, що ви можете легко розділити велику таблицю на одну таблицю на рік. Ви створюєте UNION ALLподання, яке поєднує всі щорічні таблиці. Кожна таблиця матиме власну гістограму. Зауважте, що нова додаткова статистика, представлена ​​в SQL Server 2014, дозволяє лише оновлення статистики бути більш ефективними. Оптимізатор запитів не використовуватиме статистику, створену для кожного розділу.

Тут можна запустити ще багато тестів, тому я закликаю вас експериментувати. Я зробив це тестування на SQL Server 2014 express, тому насправді нічого не зупиняє вас.


Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.