Почему мой индекс не используется автоматически? - PullRequest
4 голосов
/ 05 марта 2012

У меня есть таблица

Archive(VarId SMALLINT, Timestamp DATETIME, Value FLOAT)

VarId не уникален.Таблица содержит измерения.У меня кластерный индекс на Timestamp.Теперь у меня есть требование найти измерение для определенного VarId до определенной даты.Поэтому я делаю:

SELECT TOP(1) * 
FROM Archive 
WHERE VarId = 135 
  AND Timestamp < '2012-06-01 14:21:00'
ORDER BY Timestamp DESC; 

Если такого измерения нет, этот запрос выполняет поиск по всей таблице.Поэтому я ввел еще один индекс для (VarId, Timestamp).

Моя проблема в том, что SQL Server, кажется, не заботится об этом, запрос все еще выполняется вечно.Когда я в явном виде заявляю 'WITH (INDEX = <id>)', это работает как надо.Что я могу сделать, чтобы SQL Server использовал мой индекс автоматически?

Я использую SQL Server 2005.

Ответы [ 3 ]

3 голосов
/ 05 марта 2012

Есть разные возможности с этим. Я постараюсь помочь вам выделить их:

  1. Возможно, SQL Server предпочитает ваш кластерный индекс (весьма вероятно, что это первичный ключ) перед вашим вновь созданным индексом. Одним из способов решения этой проблемы является наличие некластеризованного первичного ключа и кластеризация индекса по двум другим полям (varid и timestamp). То есть, если вы не хотите, чтобы varid и timestamp были PK.

  2. Также может помочь просмотр (оценочного) плана выполнения.

  3. Но я считаю, что # 1 хорошо работает, только если эти 2 поля являются наиболее часто используемым (запрашиваемым) индексом. Чтобы выяснить, так ли это, было бы хорошо проанализировать, какие пользователи индекса наиболее вероятно используют (с http://sqlblog.com/blogs/louis_davidson/archive/2007/07/22/sys-dm-db-index-usage-stats.aspx):

<code>  select
          ObjectName = object_schema_name(indexes.object_id) + '.' + object_name(indexes.object_id),
          indexes.name,
          case when is_unique = 1 then 'UNIQUE ' else '' end + indexes.type_desc,
          ddius.user_seeks,
          ddius.user_scans,
          ddius.user_lookups,
          ddius.user_updates
        from
          sys.indexes
        left join sys.dm_db_index_usage_stats ddius on (
          indexes.object_id = ddius.object_id
            and indexes.index_id = ddius.index_id
            and ddius.database_id = db_id()
        )<br>
        WHERE
          object_schema_name(indexes.object_id) != 'sys' -- exclude sys objects
            AND object_name(indexes.object_id) LIKE 'Archive'
        order by
          ddius.user_seeks + ddius.user_scans + ddius.user_lookups
        desc
Удачи
1 голос
/ 05 марта 2012

Полагаю, проблема в дизайне вашего индекса.У вас есть индекс CLUSTERED для поля DATETIME, и я подозреваю, что это не уникальные данные, во многом как VarId, и, следовательно, вы не объявили его как UNIQUE.Поскольку оно не уникально, имеется скрытое 4-байтовое поле «уникализатора» (так что каждая строка может быть физически уникальной независимо от того, что вы не предоставили ей уникальные данные), и строки с одинаковым значением DATETIME по существу случайны в группеодни и те же значения DATETIME (поэтому даже для сокращения времени все равно требуется сканирование через эту группу).У вас также есть NONCLUSTERED индекс для VarId, Timestamp.Индексы NONCLUSTERED включают данные из индекса CLUSTERED, поэтому внутренне ваш индекс NONCLUSTERED действительно: VarId, Timestamp, Timestamp (из индекса CLUSTERED).Таким образом, вы могли бы опустить столбец Timestamp в индексе NONCLUSTERED, и все это было бы то же самое для оптимизатора, но в некотором смысле это было бы лучше, так как это был бы меньший индекс.

Так что вашфизический макет основан на дате, а значения VarId распределены по этим датам.Следовательно, VarId = 135 может распространяться очень далеко друг от друга с точки зрения страниц данных.Да, ваш некластеризованный индекс группирует их вместе, но оптимизатор, вероятно, рассматривает тот факт, что вам нужны все поля (часть «SELECT *») и метка времени <'2012-06-01 14:21:00условие в дополнение к этому, кажется, получает большую часть того, что вам нужно, в отличие от поиска нескольких строк и выполнения поиска закладок, чтобы получить поле «Значение» для выполнения «SELECT *».Вполне возможно, что если вы просто нажмете «SELECT TOP (1) VarId, Timestamp»), он, скорее всего, будет использовать ваш индекс NONCLUSTERED без подсказки «INDEX =». </p>

Другая проблема, влияющая на производительность в целом, может заключаться в том, что ORDER BYзапрашивает метку времени в порядке DESC, и если у вас есть индекс CLUSTERED в порядке ASC, то это будет направление, противоположное тому, что вы ищете (по крайней мере, в этом запросе).Конечно, в этом случае было бы хорошо иметь метку времени в индексе NONCLUSTERED, если бы она была в порядке DESC.

Мой совет - переосмыслить индекс CLUSTERED.Судя только по одному этому запросу (другие запросы / использование могут изменить рекомендацию), попробуйте отбросить индекс NONCLUSTERED и заново создать индекс CLUSTERED с полем Timestamp, в порядке DESC, а также с VarId, чтобы его можно было исключить из UNIQUE.Итак:

CREATE UNIQUE CLUSTERED INDEX [UIX_Archive_Timestamp_VarId] 
ON Archive (Timestamp DESC, VarId ASC)

Это, конечно, предполагает, что комбинация Timestamp и VarId уникальна.Если нет, то все равно попробуйте это без ключевого слова UNIQUE.

Обновление:

Чтобы собрать всю эту информацию и рекомендации:

При разработке индексов вам необходимо учитыватьРаспределение данных и варианты использования для взаимодействия с ними.Чаще всего есть ОЧЕНЬ МНОГО, и несколько разных подходов окажутся хорошими в теории.Вам нужно попробовать несколько подходов, профилировать / протестировать их и посмотреть, какие из них лучше всего работают в реальности.Не существует подхода «всегда делай это», не зная всех аспектов того, что ты делаешь и что еще происходит, и что еще планируется использовать и / или модифицировать эту таблицу, которая, как я подозреваю, не была представлена ​​в исходном вопросе.

Итак, чтобы начать путешествие, вы упорядочиваете записи по дате и просматриваете диапазоны дат, и даты естественным образом появляются по порядку, поэтому использование метки времени в первую очередь дает больше преимуществ от того, что вы делаете, и имеет меньшую фрагментацию, особенно если определяется как DESC вСОЗДАТЬ.В таком случае наличие NC-индекса только для VarId будет хорошо, даже если он распределен, для просмотра набора строк для определенного VarId.Так что, возможно, начать там (изменить порядок направления индекса CLUSTERED и удалить метку времени из индекса NC).Посмотрите, как эти изменения сравниваются с существующей структурой.Затем попробуйте переместить поле VarId в индекс CLUSTERED и удалите индекс NC.Вы говорите, что комбинация также не уникальна, но увеличивает предсказуемость порядка строк.Посмотрите, как это работает.Обновляется ли когда-нибудь эта таблица?Если нет, и если поле Значение вместе с Timestamp и VarId будет уникальным, попробуйте добавить это в индекс CLUSTERED и обязательно создайте его с ключевым словом UNIQUE.Посмотрите, как работают эти разные подходы, посмотрев фактический план выполнения, и используйте SET STATISTICS IO ON перед выполнением запроса и посмотрите, как сравниваются логические операции чтения между различными подходами.

Надеюсь, это поможет:)

0 голосов
/ 05 марта 2012

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

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