Самый быстрый способ для этого запроса (Какова лучшая стратегия), учитывая диапазон дат - PullRequest
8 голосов
/ 05 августа 2009

У меня есть таблица A с начальными и конечными датами в виде 2 столбцов даты и времени, помимо некоторых других. У меня есть другая таблица B, в которой есть один столбец даты и времени, называемый столбцом дат. Это в SQL Server 2005.

Здесь вопрос: как лучше настроить индексы и т. Д., Чтобы получить следующее:

select ....
 from A , B
where A.startDate >= B.dates
  and A.endDate < B.dates

Обе таблицы имеют несколько тысяч записей.

Ответы [ 10 ]

7 голосов
/ 05 августа 2009

Обновление:

См. Эту статью в моем блоге для эффективной стратегии индексации вашего запроса с использованием вычисляемых столбцов:

Основная идея заключается в том, что мы просто вычисляем округленные length и startDate для вас диапазонов, а затем ищем их, используя условия равенства (которые подходят для B-Tree индексов)


В MySQL и SQL Server 2008 вы можете использовать SPATIAL индексы (R-Tree).

Они особенно хороши для таких условий, как «выбрать все записи с заданной точкой в ​​пределах диапазона записи», что как раз для вас.

Вы сохраняете start_date и end_date как начало и конец LineString (преобразовывая их в UNIX метки времени другого числового значения), индексируете их с индексом SPATIAL и ищите все таких LineString s, минимальная ограничивающая рамка которых (MBR) содержит значение даты, о котором идет речь, с использованием MBRContains.

Смотрите эту запись в моем блоге о том, как это сделать, в MySQL:

и краткий обзор производительности для SQL Server:

Такое же решение может быть применено для поиска данного IP по сетевым диапазонам, хранящимся в базе данных.

Эта задача, наряду с вашим запросом, является другим часто используемым примером такого условия.

Обычные B-Tree индексы не годятся, если диапазоны могут перекрываться.

Если они не могут (и вы это знаете), вы можете использовать блестящее решение, предложенное @AlexKuznetsov

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

Если у вас много записей в B и мало записей в A, вы можете просто создать индекс для B.dates и позволить TS/CIS для A перейти.

Этот запрос всегда будет читать все строки из A и будет использовать Index Seek в B.dates во вложенном цикле.

Если ваши данные распространяются другим способом, i. е. у вас есть много строк в A, но мало в B, и диапазоны, как правило, короткие, тогда вы можете немного изменить дизайн таблиц:

A

start_date interval_length

, создайте составной индекс для A (interval_length, start_date)

и используйте этот запрос:

SELECT  *
FROM    (
        SELECT  DISTINCT interval_length
        FROM    a
        ) ai
CROSS JOIN
        b
JOIN    a
ON      a.interval_length = ai.interval_length
        AND a.start_date BETWEEN b.date - ai.interval_length AND b.date
3 голосов
/ 05 августа 2009
2 голосов
/ 05 августа 2009

Я работал в двух компаниях (занимающихся системами управления временем и посещаемостью), которые много раз работали со столбцами startDate и endDate. По моему опыту нет хороших показателей, что всегда работает с диапазонами дат .

Попробуйте индексы, такие как (startDate, -endDate) и (-endDate, startDate), чтобы увидеть, помогают ли они, многое зависит от того, на что похожи данные в таблице . Например, если у вас есть много старых строк с endDate до дат, которые вы ищете, может помочь Sql использовать индекс, основанный на (endDate, startDate).

Также попробуйте использовать индекс, охватывающий все столбцы, которые содержатся в вашем выражении «where», поэтому sql не нужно читать основную таблицу, пока не будет определено, какие строки возвращать.

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

Расширение данных, поэтому у вас есть таблица, содержащая (дата, строка) со строкой для каждой даты в диапазоне дат может потребоваться . Однако поддерживать таблицу «index» в актуальном состоянии - это непросто.

Если вы знаете, что некоторые диапазоны дат не перекрываются, взгляните на Использование CROSS APPLY для оптимизации объединений в условиях МЕЖДУ (Например, записи о заболеваниях сотрудника могут не совпадать)

В конце концов, если у вас всего несколько тысяч записей, полное сканирование таблицы неплохо.

Quassnoi субъекты, использующие SPATIAL-индексы , у меня нет опыта «злоупотреблять» пространственными индексами таким образом, но я думаю, что стоит попробовать. Тем не менее, будьте очень осторожны, если вам придется поддерживать всех поставщиков баз данных, использующих несколько баз данных, поскольку пространственный индекс довольно нов. Также вам могут понадобиться столбцы даты для инструментов отчетности и т. Д.

(Рано или поздно понадобится найти все строки, которые перекрывают диапазон дат, тогда будет еще сложнее получить индексы, которые возвращают хорошие результаты.)

0 голосов
/ 05 августа 2009

Я бы пошел с этим

CREATE CLUSTERED INDEX IX_DateRange ON dbo.A
    (
    StartDate,
    EndDate DESC
    ) 
GO
0 голосов
/ 05 августа 2009

Требуется немного больше информации. Сколько других столбцов в таблицах? Эти существующие таблицы с большим количеством запросов уже идут против них, или все новые таблицы? Какие проблемы с производительностью вы видите, что побуждает вас задать вопрос?

Я предполагаю, что все три столбца НЕ ПУСТО (не только для синтаксиса запроса, но и для полезности индекса).

Я бы начал с составного индекса для A.startDate + A.endDate и другого индекса для B.dates (но это, вероятно, не требуется). Если только эти даты не являются основной целью таблиц, я бы не стал создавать кластерные индексы для этих столбцов. Это вдвойне верно, если эти таблицы являются существующими таблицами, к которым выполняются другие запросы. Предыдущие запросы могут быть написаны с учетом существующих кластеризованных индексов.

0 голосов
/ 05 августа 2009

Я бы просто добавил кластерный индекс на B.dates. Если вы добавите индексы в startDate и endDate, он ничего не купит, потому что вы все равно будете сканировать индексы на A. Кластерный индекс на B дает вам поиск индекса по крайней мере в B. Сканирование таблиц и сканирование индексов - это одно и то же, поэтому нет смысла добавлять индексы, чтобы вывести слово Table Scan из вашего плана выполнения:)

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

0 голосов
/ 05 августа 2009

Следующий скрипт выведет список возможных пропущенных индексов (вы можете отфильтровать оператор по t.name).

SELECT     t.name AS 'affected_table',
           'Create NonClustered Index IX_' + t.name + '_missing_' + CAST(ddmid.index_handle AS VARCHAR(10)) + ' On ' + ddmid.STATEMENT + ' (' + ISNULL(ddmid.equality_columns, '') +
           CASE
                      WHEN ddmid.equality_columns IS NOT NULL
                             AND ddmid.inequality_columns IS NOT NULL
                      THEN ','
                      ELSE ''
           END + ISNULL(ddmid.inequality_columns, '') + ')' + ISNULL(' Include (' + ddmid.included_columns + ');', ';') AS sql_statement,
           ddmigs.user_seeks,
           ddmigs.user_scans,
           CAST((ddmigs.user_seeks + ddmigs.user_scans) * ddmigs.avg_user_impact AS INT) AS 'est_impact',
           ddmigs.last_user_seek
FROM       sys.dm_db_missing_index_groups      AS ddmig
INNER JOIN sys.dm_db_missing_index_group_stats AS ddmigs
ON         ddmigs.group_handle = ddmig.index_group_handle
INNER JOIN sys.dm_db_missing_index_details AS ddmid
ON         ddmig.index_handle = ddmid.index_handle
INNER JOIN sys.tables AS t
ON         ddmid.OBJECT_ID = t.OBJECT_ID
WHERE      ddmid.database_id = DB_ID()
       AND CAST((ddmigs.user_seeks + ddmigs.user_scans) * ddmigs.avg_user_impact AS INT) > 100
ORDER BY   CAST((ddmigs.user_seeks + ddmigs.user_scans) * ddmigs.avg_user_impact AS INT) DESC;
0 голосов
/ 05 августа 2009

Вам нужно 3 индекса A.startDate, B.dates и A.endDate, может быть индекс (A.endDate + A.startDate) также хорош. У меня нет сведений о других столбцах и целях этих таблиц, но я рассмотрю возможность использования кластерного индекса.

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

0 голосов
/ 05 августа 2009

каждая версия sql server 2000, 2005, 2008 имеет программу под названием «Советник по настройке базы данных», когда вы запускаете какой-либо запрос, он сообщает, какие индексы нужно добавить, чтобы получить запрос быстрее С уважением, Йордан

0 голосов
/ 05 августа 2009

Если вам нужно оптимизировать, попробуйте выполнить этот запрос в Query Analyzer.

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