Рекомендуемые индексы для запроса в большой таблице, включающей "диапазон дат" и "идентификатор заказа"

У меня есть запрос (который был создан 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 И OrderId
  • Какая-то другая комбинация?

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

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

ReactJs | Supabase | Добавление данных в базу данных
ReactJs | Supabase | Добавление данных в базу данных
Это и есть ваш редактор таблиц в supabase.👇
Понимание Python и переход к SQL
Понимание Python и переход к SQL
Перед нами лабораторная работа по BloodOath:
0
0
4 860
5

Ответы 5

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

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

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

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

Следует ли индексировать битовое поле в SQL Server?

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

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

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

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

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

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

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

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

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

Я бы не стал включать такое количество кулменов в индекс, если бы не доказано, что это 100% необходимость. Если вы используете SQL Server 2005, вам лучше ВКЛЮЧИТЬ столбцы, которые удаляют поиск книги, в кластеризованный индекс (кроме того, домен находится в другой таблице!)

Mitch Wheat 23.11.2008 12:48

Я бы создал индекс для столбцов 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
}

Ссылка.

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

+1 за отличный совет по индексу, -0,5 за предложение NOLOCK; это просто будет поощрять использовать его все время. Лучше настроить схемы индексации на меньшее количество прочитанных записей, чем даже пытаться использовать НЕОБХОДИМЫЙ маршрут.

SqlACID 23.11.2008 18:50

@SqlACID: вот почему я поставил «Предостережение: используйте подсказки NOLOCK только там, где у вас есть очень веская причина»!

Mitch Wheat 24.11.2008 03:49

отчеты могут "выдержать" незафиксированные строки? ХМ? Это все равно, что сказать, что наука прекрасно ладит с горсткой неправдивых фактов. Серьезно, люди, использующие SQL Server, совершенно иначе воспринимают «ИСТИНУ». Что, если откатится? У вас будет отчет, включающий эту запись, и никакой записи нигде.

Mark Brady 03.12.2008 00:31
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, лучше всего будет использовать кластеризованный индекс по дате. Это должно дать вам сканирование кластерного индекса с результатами, упорядоченными для вашей TOP 1000.

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

Другие вопросы по теме