У меня вопрос по индексам SQL Server. Я не администратор базы данных и предполагаю, что ответ ясен для тех из вас, кто есть. Я использую SQL Server 2008.
У меня есть таблица, которая похожа на следующую (но имеет больше столбцов):
CREATE TABLE [dbo].[Results](
[ResultID] [int] IDENTITY(1,1) NOT NULL,
[TypeID] [int] NOT NULL,
[ItemID] [int] NOT NULL,
[QueryTime] [datetime] NOT NULL,
[ResultTypeID] [int] NOT NULL,
[QueryDay] AS (datepart(day,[querytime])) PERSISTED,
[QueryMonth] AS (datepart(month,[querytime])) PERSISTED,
[QueryYear] AS (datepart(year,[querytime])) PERSISTED,
CONSTRAINT [PK_Results] PRIMARY KEY CLUSTERED
(
[ResultID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, FILLFACTOR = 90) ON [PRIMARY]
) ON [PRIMARY]
Важные поля, на которые следует обратить внимание: ResultID, первичный ключ, и QueryTime - дата и время, когда был получен результат.
У меня также есть следующий индекс (среди прочих):
CREATE NONCLUSTERED INDEX [IDX_ResultDate] ON [dbo].[Results]
(
[QueryTime] ASC
)
INCLUDE ( [ResultID],
[ItemID],
[TypeID]) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, FILLFACTOR = 90) ON [PRIMARY]
В базе данных, где у меня около миллиона строк в таблице, индекс используется при выполнении запроса, такого как:
select top 1 * from results where querytime>'2009-05-01' order by ResultID asc
В другом экземпляре той же базы данных с 50 миллионами строк SQL Server решает не использовать индекс, а скорее выполняет сканирование кластеризованных индексов, которое оказывается ужасно медленным. (и скорость зависит от даты). Даже если я использую подсказки запроса, чтобы заставить его использовать IDX_ResultDate, он все еще немного медленный и тратит 94% своего времени на сортировку по ResultID. Я подумал, что, создав индекс с ResultID и QueryTime в качестве отсортированных столбцов в индексе, я смог ускорить свой запрос.
Поэтому я создал следующее:
CREATE NONCLUSTERED INDEX [IDX_ResultDate2] ON [dbo].[Results]
(
[QueryTime] ASC,
[ResultID] ASC
)
INCLUDE ( [ItemID],
[TypeID]) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, FILLFACTOR = 90) ON [PRIMARY]
GO
Я предполагал, что сначала будет использоваться сортировка по QueryTime, чтобы найти совпадающие результаты, которые уже будут отсортированы по ResultID. Однако это не так, так как этот индекс не меняет производительности по сравнению с существующим.
Затем я попробовал следующий индекс:
CREATE NONCLUSTERED INDEX [IDX_ResultDate3] ON [dbo].[Results]
(
[ResultID] ASC,
[QueryTime] ASC
)
INCLUDE ( [ItemID],
[TypeID]) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, FILLFACTOR = 90) ON [PRIMARY]
GO
Этот дает ожидаемый результат. Похоже, чтобы вернуться в постоянное время (доли секунды).
Однако я озадачен, почему IDX_ResultDate3 работает хорошо, тогда как IDX_ResultDate2 - нет.
Я бы предположил, что бинарный поиск в отсортированном списке QueryTime с последующим просмотром первого результата в его дочернем списке ResultIDs - самый быстрый способ получения результата. (Отсюда мой начальный порядок сортировки).
Дополнительный вопрос: должен ли я создать постоянный столбец с частью даты QueryTime и индексом по ней (у меня уже есть три постоянных столбца, как вы можете видеть выше)?