Навеяно постом 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