2016-11-02 2 views
3

В нашей базе данных имеется множество таблиц с данными, актуальными только в течение определенного периода времени. Например, контракты, у них есть start_date и end_date. И это не обязательно полные месяцы.как смоделировать данные/индексы для быстрого поиска временных рядов

Сейчас это типичный тип запроса к таблице:

SELECT 
    * 
FROM 
    contracts c 
WHERE 
     c.start_date <= :1 
    AND c.end_date >= :2 
    AND c.region_id = :3 

Поскольку мы имеем 20 лет данных в нашей таблице (~ 7000 дней), дата очень хорошие критерии фильтрации, особенно в следующих случаях: 1 и: 2 - в тот же день. Region_id не является таким хорошим критерием фильтрации, потому что их не так много (~ 50). В этом примере мы имеем (в том числе) 2 Индексы на нашем столе:

contracts_valid_index (start_date, end_date) 
contracts_region (region_id) 

К сожалению, выше запрос будет часто нас индекс contracts_region, потому что оптимизатор считает, что это дешевле. Причина этого проста: когда я выбираю день в середине наших данных, тогда база данных будет думать, что индекс над start_date не будет действительно хорошим, потому что он будет отфильтровывать только половину данных. И, посмотрев на end_date, это тоже самое. Поэтому оптимизатор считает, что он может отфильтровать только 1/4 моих данных. Поскольку он не знает, что start_date и end_date обычно довольно близко друг к другу, и этот индекс будет очень избирательным.

План выполнения с использованием contract_valid_index имеет более высокие затраты, чем план выполнения с использованием contract_region. Но в действительности contract_valid_index намного лучше.

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

Любые предложения?

ответ

1

Поскольку вы указать, что вы используете Oracle 12c может помочь определить ваш Дата_начала и столбцы датой_окончания в temporal действительных столбцов времени при условии, что они соответствуют соответствующим временным Семантика достоверности (start_date и end_date должны быть метками времени, end_date должно быть> start_date или возможно, нулевые и действительные периоды времени включают дату начала, но исключают дату окончания, то есть это частично закрытый/открытый диапазон, в отличие от обычного между оператором, который обозначает полностью закрытый диапазон). Например:

ALTER TABLE contracts ADD (PERIOD FOR valid_time (start_date, end_date)); 

Вы можете запросить таблицу контрактов на определенный период действия Thusly:

SELECT 
    c.* 
FROM 
    contracts VERSIONS PERIOD FOR valid_time BETWEEN :1 AND :2 c 
WHERE 
    c.region_id = :3 

Это семантический аналогично:

SELECT 
    c.* 
FROM 
    contracts c 
WHERE 
     :1 < end_date 
    AND start_date <= :2 
    AND c.region_id = :3 

В качестве альтернативы для запроса записей действительны для определенного момента времени, а не для диапазона времени:

SELECT 
    c.* 
FROM 
    contracts AS OF PERIOD FOR valid_time :1 c 
WHERE 
    c.region_id = :2 

которая семантически аналогична:

SELECT 
    c.* 
FROM 
    contracts c 
WHERE 
     :1 BETWEEN start_date AND end_date 
    and :1 <> end_date 
    and c.region_id = :2 

Я не уверен, если нулевые значения для датой_начала и датой_окончания указывают на начало и конец времени или, соответственно, не так как я в настоящее время нет экземпляра R12, чтобы проверить в.

+0

Это то, что я искал. Мне не нравится открытый конец интервала, но это не должно быть проблемой. Но я сомневаюсь, что я получу поддержку JPA для этого – EasterBunnyBugSmasher

+0

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

+0

Что касается открытых и закрытых диапазонов, я работал с ними и предпочитаю открытые. Таким образом, когда один диапазон заканчивается, а затем начинается, вы можете использовать ту же дату для конца, как и для следующего запуска, и не беспокоиться о перекрытии. И если вы используете усеченные значения для ваших дат начала и окончания, вам не нужно беспокоиться о том, что необработанная проверка диапазона падает в промежутке между одним диапазоном и началом следующего. – Sentinel

1

Я ранее сталкивался с той же проблемой использования индекса по отношению к большим наборам IP-адресов в базах данных MySQL (нестись со мной, это действительно та же проблема).

The solution Я нашел (по большому счету, я не беру на себя ответственность за изобретение), должен был использовать геопространственный индекс. Это специально предназначено для поиска данных в пределах диапазонов. Большинство реализаций (в том числе и в mysql) жестко привязаны к двумерному пространству, в то время как ip-адреса и время 1-мерные, но его тривиально отображать 1-мерную координату в двумерное пространство (см. Ссылку для пошагового объяснения) ,

Извините, я ничего не знаю о геопространственных возможностях Oracle, поэтому я не могу предложить какой-либо пример кода, но он поддерживает геопространственное индексирование, поэтому может эффективно решать ваши запросы.

0

Вы можете попробовать следующий запрос, чтобы увидеть, если он работает лучше:

WITH t1 AS (
    SELECT * 
    FROM contracts c 
    WHERE c.start_date <= :1 
     AND c.end_date >= :2 
) 
SELECT * 
    FROM t1 
    WHERE c.region_id = :3 

Хотя это, скорее всего, предотвратить возможность использования индекса contracts_region.

В качестве альтернативы вы можете попробовать намекая запрос использовать требуемый индекс:

SELECT /*+ INDEX(c contracts_valid_index) */ 
    * 
FROM 
    contracts c 
WHERE 
     c.start_date <= :1 
    AND c.end_date >= :2 
    AND c.region_id = :3 

Или намекая его не использовать нежелательную индекс:

SELECT /*+ NO_INDEX(c contracts_region) */ 
    * 
FROM 
    contracts c 
WHERE 
     c.start_date <= :1 
    AND c.end_date >= :2 
    AND c.region_id = :3 

При тестировании это для себя, не используя подсказки Я обнаружил, что при выборе дат вблизи начала или конца доступного диапазона дат оптимизатор использовал подсказку INDEX_RS_ASC. Добавим, что в запросе, как показано ниже, вызванного мое тестирование, чтобы использовать нужный индекс, даже когда диапазон дат был ближе к центру диапазона дат:

SELECT /*+ INDEX_RS_ASC(c contracts_valid_index) */ 
    * 
FROM 
    contracts c 
WHERE 
     c.start_date <= :1 
    AND c.end_date >= :2 
    AND c.region_id = :3 

Мои данные выборки состояли из 10000000 рядов равномерно распределенных accross 50 регионов и 1000 лет каждый с 30-дневным допустимым диапазоном.

Смежные вопросы