выберите самые последние значения в очень большой таблице - PullRequest
0 голосов
/ 08 сентября 2018

Я - рабочий, которому поручено извлекать данные из очень большой таблицы. Я не администратор базы данных и не могу разделить его или изменить индексирование. Таблица содержит почти миллиард записей, не разбита на разделы и, вероятно, может быть проиндексирована «лучше». Мне нужно два поля, которые мы будем называть mod_date и obj_id (mod_date индексируется). РЕДАКТИРОВАТЬ: я также добавляю фильтр для «клиента», который я размыл на скриншоте плана объяснения.

Мои данные: В группе из почти миллиарда строк у нас есть менее 10 000 значений obj_id для запроса в течение нескольких лет (некоторые из них могут даже быть NULL). Некоторые из <10k obj_ids - вероятно, от 1000 до 2500 - имеют более 10 миллионов значений mod_date каждое. Когда у obj_ids более нескольких миллионов значений mod_date, каждому obj_id требуется несколько минут для сканирования и сортировки с использованием MAX (mod_date). Полный набор результатов для запроса занимает более 12 часов, и никто не завершил его без какой-либо «проблемы» (заблокированный, отключенный ноутбук и т. Д.). Даже если мы получим первые 50 возвращенных строк, нам все равно нужно будет экспортировать их в Excel ... всего будет около 8000 строк с 2 столбцами, но мы никогда не сможем дойти до конца. </p>

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

select MAX(trunc(mod_date,'dd')) as last_modified_date, obj_id
from my_table
where client = 'client_name'
and obj_type_id = 12
group by obj_id;

Количество элементов 317917582, «Стоимость» 12783449

explain plan

Выпуск: Проблема заключается в скорости запроса с такой большой неразделенной таблицей, учитывая текущие индексы. Все остальные ответы, которые я видел о «самой последней дате», имеют тенденцию использовать MAX, возможно, в сочетании с FIRST_VALUE, которые, кажется, требуют полного сканирования всех строк, чтобы отсортировать их и затем определить, какая из них самая последняя.

Я надеюсь, что есть способ избежать этого, чтобы ускорить результаты. Похоже, что Oracle (я использую Oracle SQL developer) должен иметь возможность взять obj_id, найти самую последнюю строку mod_date, начиная с «сейчас» и работать в обратном направлении, и двигаться дальше, как только найдет какое-либо значение mod_date ... потому что это свидание. Есть ли способ сделать это?

Даже при такой большой таблице obj_ids, имеющей менее 10000 mod_dates, может очень быстро вернуть MAX (mod_date) (секунды или меньше). Проблема, с которой мы сталкиваемся, заключается в том, что obj_ids с наибольшим количеством mod_dates (более 10 миллионов) дольше всего сканирует и сортирует, когда они «должны» быть самыми быстрыми, если я смогу заставить Oracle начать сначала смотреть на самые последние… потому что это быстро найти последнюю дату и двигаться дальше!

Ответы [ 2 ]

0 голосов
/ 08 сентября 2018

Тбоне это правильно, я думаю. Или, если у вас нет полномочий для создания материализованного представления, как он предлагает, вы можете создать сценарий оболочки на сервере базы данных для запуска вашего запроса через SQL * Plus и spool вывод в файл. Затем запустите этот скрипт, используя nohup, и вам не нужно беспокоиться об отключении ноутбуков и т. Д.

Но я хотел кое-что объяснить в вашем комментарии:

Oracle должен иметь возможность взять obj_id, найти самую последнюю строку mod_date, начиная с «сейчас» и работать в обратном направлении, и двигаться дальше, как только найдет какое-либо значение mod_date… потому что это дата. Есть ли способ сделать это?

Это было бы ужасным способом для Oracle выполнить ваш запрос, учитывая индексы, которые вы перечислили. Давайте пройдем через это ...

На obj_id нет индекса, поэтому Oracle необходимо выполнить полное сканирование таблицы, чтобы убедиться, что оно получает все различные значения obj_id.

Итак, он запускает FTS и находит obj_id 101. Затем он говорит: «Мне нужно max(mod_date) для 101 ... ах-ха! У меня есть индекс!» Итак, он выполняет обратное сканирование индекса. Для каждой записи в индексе она ищет строку из таблицы и проверяет, является ли она obj_id 101. Если obj_id недавно был обновлен, то у нас все хорошо, потому что мы находим его и рано останавливаемся. Но если obj_id не обновлялся долгое время, нам нужно прочитать много записей индекса и для каждого получить доступ к строкам таблицы, чтобы выполнить проверку.

В худшем случае - если obj_id является одним из тех немногих, что вы упомянули, где max(mod_date) будет NULL, мы будем использовать индекс для поиска КАЖДОЙ ОДНОЙ СТРОКИ в вашей таблице, в которой нет null mod_date.

Выполнение такого количества поисков по индексу было бы ужасным планом, если бы он делал это только один раз, но вы говорите о том, чтобы делать это для нескольких старых или никогда не обновляющихся obj_id значений.

Во всяком случае, все это академично. Не существует плана запросов Oracle, который будет выполнять запрос таким образом. Это не зря.

Без лучшего индексирования вы просто не улучшите ни одного полного сканирования таблицы.

0 голосов
/ 08 сентября 2018

Во-первых, я бы сказал, что распространенное заблуждение состоит в том, что для ускорения выполнения запроса вам нужен индекс (или лучшие индексы). Полное сканирование таблицы имеет смысл, когда вы извлекаете более 10% данных (приблизительная оценка, зависит от количества считанных блоков, размера блока и т. Д.).

Мой совет - настроить материализованное представление (MY_MV или любое другое), которое просто выполняет группирование по запросу (по всем идентификаторам). Если вам нужно ограничить число идентификаторов 10k, просто убедитесь, что вы полностью отсканировали таблицу (см. План объяснения). При необходимости вы можете добавить полную подсказку (выберите / * + full (t) * / .. из big_table t ...)

Затем выполните:

dbms_mview.refresh('MY_MV','C',atomic_refresh=>false);

Вот и все. Нет проблем с клиентом, который возвращает только первые x строк, и когда вы берете все данные, он перезапускает весь запрос (тьфу). Полные сканы также легче отследить в длинных операциях (например, сложнее сказать, каков был ваш прогресс, если вы выполняете вложенные циклы в индексе).

Как только это будет сделано, выведите всю таблицу MV в файл или что вам нужно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...