Тетрадочка DBA

Хозяйке на заметку — SELECT … SAMPLE

Posted by shane54lv на 9 Июль, 2009

Навеяно постом Elic‘а из обсуждения «грубый count».

Узнал про интереснейшую фичу в Оракле. Есть возможность указать запросу SELECT прочитать лишь часть таблицы (sample) для того, чтобы получить примерный результат. Это нужно, например, чтобы узнать примерное количество строк в таблице. Если их там миллиард, понятно, что девятьсот миллионов или миллиард сто миллионов — разница не сильно большая, а вот count (*) будет работать сутки. Ну и т.д., примеров как это использовать можно придумать много.

Теория

Тип sample может быть как обычно, строки или блоки. Т.к. sample — это лишь предположение, результат от запроса к запросу всегда будет различаться, но можно заставить базу выдавать всегда одно и то же предположение — SEED.

И последнее. При использовании block sample, запрос обязательно должен идти через FTS или FFS. Если существует другой, более оптимальный путь, Оракл будет использовать его и вместо block sample результат будет выдан на основании row sample. Соответственно, чтобы форсировать block sample, необходимо хинтом указать использование фуллскана (хинт +FULL или +INDEX_FFS).

Практика

Берем таблицу KNS.KNS_LOG:

SELECT ROUND (t1.BYTES / 1024 / 1024 / 1024, 0) AS "Gb"
  FROM dba_segments t1
 WHERE t1.owner = 'KNS' AND t1.segment_name = 'KNS_LOG';

Результат — 28 Гб, самое то :) На продукции count(*) я запускать не стану, но зная количество блоков таблицы и скорость нашей подсистемы I/O (очень приличной, кстати), можно предположить, что работать будет долго. Точнее считать лень, да и не суть.

Теперь магия:

SELECT COUNT (1) * 10000 FROM kns.kns_log SAMPLE BLOCK (0.01);

Пояснение:

  • BLOCK (0.01) — используя блочную выборку, прочитать одну десятитысячную блоков таблицы
  • COUNT (1) * 10000 — соответственно, результат умножаем на десять тысяч

Запрос работает менее пол-секунды, результат 188 190 000. Повторные запуски:


188 190 000
153 360 000
149 630 000
129 950 000
229 950 000

Т.е. результат бросает из стороны в сторону очень значительное, в процентном отношении считать опять таки лень, но на глаз +/- 25%. Хотя это нормально для выборки 0.01% данных!

И последнее. Как я говорил выше, чтобы закрепить какое-то одно значение выборки, используем конструкцию SEED ():

SELECT COUNT (1) * 10000 FROM kns.kns_log SAMPLE BLOCK (0.01) SEED (5);

Число 5 — от балды, берем любую цифру, это просто константа для привязки результата запроса. Результат — 205 800 000 и при повторных выполнениях сохраняется, что и требовалось доказать.

И совсем последнее — проверка правильности результата:

SELECT * FROM dba_tables t1 WHERE t1.owner = 'KNS' AND t1.table_name = 'KNS_LOG';

Значение NUM_ROWS: 198 153 830 (статистика собиралась в воскресенье, два дня назад, после чего в понедельник работал джоб, подтирающий самые старые записи).

SELECT * FROM dba_indexes t1 WHERE t1.owner = 'KNS' AND t1.table_name = 'KNS_LOG';

Значение DISTINCT_KEYS для уникального индекса: 143 667 320.

Т.е. результаты запроса с SAMPLE похожи на правду.

Ссылка на доку: http://download.oracle.com/docs/cd/B19306_01/server.102/b14200/statements_10002.htm#i2065953

Добавить комментарий

Заполните поля или щелкните по значку, чтобы оставить свой комментарий:

Логотип WordPress.com

Для комментария используется ваша учётная запись WordPress.com. Выход / Изменить )

Фотография Twitter

Для комментария используется ваша учётная запись Twitter. Выход / Изменить )

Фотография Facebook

Для комментария используется ваша учётная запись Facebook. Выход / Изменить )

Google+ photo

Для комментария используется ваша учётная запись Google+. Выход / Изменить )

Connecting to %s

 
%d такие блоггеры, как: