Рекомендуемые индексы для запроса в большой таблице, включающей «диапазон дат» и «идентификатор заказа» - PullRequest
0 голосов
/ 23 ноября 2008

У меня есть запрос (который был создан LINQ to SQL), чтобы получить список «посещений сайта», которые были сделаны между определенным диапазоном дат, который привел к заказу (orderid не является нулевым).

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

Таблица приближается к миллиону строк, и мне нужны индексы, чтобы помочь мне здесь. Этот запрос используется только для отчетов, поэтому он не должен быть очень быстрым, просто не задерживать запросы других пользователей (что он и делает).

SELECT TOP 1000
  t0.SiteVisitId, t0.OrderId, t0.Date, 
  t1.Domain, t0.Referer, t0.CampaignId
FROM 
  SiteVisit AS t0
  LEFT OUTER JOIN KnownReferer AS t1 ON t1.KnownRefererId = t0.KnownRefererId
WHERE
  t0.Date <= @p0 
  AND t0.Date >= @p1
  AND t0.OrderId IS NOT NULL
ORDER BY 
  t0.Date DESC

@p0='2008-11-1 23:59:59:000', @p1='2008-10-1 00:00:00:000'

В настоящее время у меня есть кластеризованный индекс на SiteVisitId, который является моим целочисленным столбцом идентификаторов.

Я не знаю, какие из следующих действий наиболее эффективны:

  • Создать индекс для Date
  • Создать индекс на Date И отдельный индекс на OrderId
  • Создать индекс с несколькими столбцами для Date AND OrderId
  • Какая-нибудь другая комбинация?

Мне также интересно, должен ли я создать отдельный битовый столбец для hasOrder вместо проверки, если OrderId IS NOT NULL, может ли это быть более эффективным.

К вашему сведению: KnownReferer - это просто таблица, которая содержит список из 100 или около того известных HttpReferers, поэтому я могу легко увидеть, сколько хитов из Google, Yahoo и т. Д.

Ответы [ 5 ]

2 голосов
/ 23 ноября 2008

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

Я бы начал с индекса по столбцу [Date]. Если для типичного запроса у вас получилось небольшое количество строк, вам не нужно добавлять столбец [OrderId] в индекс.

С другой стороны, если у вас есть большое количество строк в обычном месяце, вы можете добавить столбец [OrderId] в индекс, хотя, поскольку он обрабатывается как логическое значение, он может не купить вам много , Это зависит от того, сколько строк NULL против NOT NULL. Если у вас есть много строк за данный месяц, но только у нескольких из них допустимый [OrderId], тогда индекс, вероятно, улучшит производительность.

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

Должен ли я индексировать битовое поле в SQL Server?

И, конечно же, протестируйте индексы и планы, созданные с и без индекса.

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

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

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

1 голос
/ 23 ноября 2008

Я бы создал индекс по столбцам Date и OrderId и INCLUDE SiteVisitId, Referer, CampaignId (при условии, что вы используете SQL Server 2005 и более поздние версии). Также создайте индекс для столбца внешнего ключа KnownRefererId.

Учитывая, что это отчетный запрос и он может противостоять нечетной незафиксированной строке, я бы предложил использовать NOLOCK (или подсказку READ UNCOMMITED):

using (var trans = new TransactionScope(TransactionScopeOption.Required,
                      new TransactionOptions
                      {
                          IsolationLevel = IsolationLevel.ReadUncommitted
                      }))
{
    // Put your linq to sql query here
}

Ref .

Предостережение : Используйте подсказки NOLOCK только в том случае, если у вас есть очень веская причина . В прошлом я видел, как разработчики терпят неудачу из-за общего использования!

0 голосов
/ 23 ноября 2008
SELECT TOP 1000
  t0.SiteVisitId, t0.OrderId, t0.Date, 
  t1.Domain, t0.Referer, t0.CampaignId
FROM 
  SiteVisit AS t0
LEFT OUTER JOIN KnownReferer AS t1 ON t1.KnownRefererId = t0.KnownRefererId
WHERE
  t0.Date <= @p0 
  AND t0.Date >= @p1
  AND t0.OrderId IS NOT NULL
ORDER BY 
  t0.Date DESC

@p0='2008-11-1 23:59:59:000', @p1='2008-10-1 00:00:00:000'

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

Если предположить, что в диапазоне дат за 1 месяц имеется много строк, и относительно немногие из них имеют OrderId IS NULL, то лучше всего использовать кластеризованный индекс для Date. Это должно дать вам сканирование кластерного индекса с результатами, которые Никели заказала для вашей TOP 1000.

Вы также можете захотеть, чтобы KnownReferer.KnownRefererId был либо кластеризованным индексом, либо комбинированным индексом с knownRefererId + Domain, чтобы избежать поиска в этой таблице. Я предполагаю, что число KnownReferers невелико - поэтому я не ожидаю от этого большой выгоды.

0 голосов
/ 23 ноября 2008

Если вы действительно хотите оптимизировать bejesus из этого запроса и можете принять более медленную вставку в таблицу, вы должны создать индекс для: -

(Date, OrderId, SiteVisitId, Domain, Referer, CampaignId)

Это позволит базе данных полностью вернуть ответ из индекса без какой-либо сортировки или доступа к отдельной таблице.

0 голосов
/ 23 ноября 2008

Также стоит подумать, нужно ли вам хранить строки в SiteVisit, у которых нет KnownRefererId в таблице KnownReferer, и которые имеют нулевой OrderId. Если они вам не нужны, измените их, удалите из таблицы и измените кластеризованный индекс на SiteVisitId и Date, и запрос должен быть довольно быстрым.

Но я уверен, что вы храните эти дополнительные строки по причине.

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