Когда я добавляю два столбца в мой выбор, запрос не отвечает. Тип столбца nvarchar(2000)
. Это немного необычно.
- Версия SQL Server - 2014.
- Существует только один первичный индекс.
- Всего записей всего 1000 строк.
Вот план выполнения до ( XML showplan ):
План выполнения после ( XML showplan ):
Вот запрос:
select top(100)
Batch_Tasks_Queue.id,
btq.id,
Batch_Tasks_Queue.[Parameters], -- this field
btq.[Parameters] -- and this field
from
Batch_Tasks_Queue with(nolock)
inner join Batch_Tasks_Queue btq with(nolock) on Batch_Tasks_Queue.Start_Time < btq.Start_Time
and btq.Start_Time < Batch_Tasks_Queue.Finish_Time
and Batch_Tasks_Queue.id <> btq.id
and btq.Start_Time is not null
and btq.State in (3, 4)
where
Batch_Tasks_Queue.Start_Time is not null
and Batch_Tasks_Queue.State in (3, 4)
and Batch_Tasks_Queue.Operation_Type = btq.Operation_Type
and Batch_Tasks_Queue.Operation_Type not in (23, 24, 25, 26, 27, 28, 30)
order by
Batch_Tasks_Queue.Start_Time desc
Общее количество результатов составляет 17 строк. Грязные данные (подсказка nolock) не важны.
Вот структура таблицы:
CREATE TABLE [dbo].[Batch_Tasks_Queue](
[Id] [int] NOT NULL,
[OBJ_VERSION] [numeric](8, 0) NOT NULL,
[Operation_Type] [numeric](2, 0) NULL,
[Request_Time] [datetime] NOT NULL,
[Description] [varchar](1000) NULL,
[State] [numeric](1, 0) NOT NULL,
[Start_Time] [datetime] NULL,
[Finish_Time] [datetime] NULL,
[Parameters] [nvarchar](2000) NULL,
[Response] [nvarchar](max) NULL,
[Billing_UserId] [int] NOT NULL,
[Planned_Start_Time] [datetime] NULL,
[Input_FileId] [uniqueidentifier] NULL,
[Output_FileId] [uniqueidentifier] NULL,
[PRIORITY] [numeric](2, 0) NULL,
[EXECUTE_SEQ] [numeric](2, 0) NULL,
[View_Access] [numeric](1, 0) NULL,
[Seeing] [numeric](1, 0) NULL,
CONSTRAINT [PKBachTskQ] PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [Batch_Tasks_QueueData]
) ON [Batch_Tasks_QueueData] TEXTIMAGE_ON [Batch_Tasks_QueueData]
GO
SET ANSI_PADDING OFF
GO
ALTER TABLE [dbo].[Batch_Tasks_Queue] WITH NOCHECK ADD CONSTRAINT [FK0_BtchTskQ_BlngUsr] FOREIGN KEY([Billing_UserId])
REFERENCES [dbo].[BILLING_USER] ([ID])
GO
ALTER TABLE [dbo].[Batch_Tasks_Queue] CHECK CONSTRAINT [FK0_BtchTskQ_BlngUsr]
GO
sql-server
query-performance
sql-server-2014
Хамид Фатхи
источник
источник
Ответы:
Резюме
Основными проблемами являются:
подробности
Эти два плана в основном очень похожи, хотя производительность может сильно отличаться:
План с дополнительными столбцами
Сначала возьмем лишние столбцы, которые не завершаются в разумные сроки:
Интересные особенности:
Start_Time
не равно нулю,State
равно 3 или 4 иOperation_Type
является одним из перечисленных значений. Таблица полностью сканируется один раз, причем каждая строка проверяется на соответствие предикатам. Только строки, которые проходят все тесты, передаются в сортировку. По оценкам оптимизатора, 38 283 строки будут соответствовать требованиям.Start_Time DESC
. Это последний порядок представления, запрошенный запросом.Start_Time
неState
равны NULL и равны 3 или 4. Предполагается, что на каждой итерации будет получено 400 875 строк. За 94,2791 итераций общее количество строк составляет почти 38 миллионов.Operation_Type
совпадает, чтоStart_Time
из узла 4 меньше, чемStart_Time
из узла 5, чтоStart_Time
из узла 5 меньше, чемFinish_Time
из узла 4, и что эти дваId
значения не совпадают.Большая неэффективность очевидно на шагах 6 и 7 выше. Полное сканирование таблицы в узле 5 для каждой итерации может быть даже незначительным, если это происходит только 94 раза, как предсказывает оптимизатор. Набор сравнений ~ 38 миллионов на строку в узле 2 также является большой стоимостью.
Важно отметить, что оценка цели в строке 93/94 также, скорее всего, будет неправильной, поскольку она зависит от распределения значений. Оптимизатор предполагает равномерное распределение при отсутствии более подробной информации. Говоря простым языком, это означает, что если ожидается, что 1% строк в таблице будет соответствовать требованиям, оптимизатор полагает, что для поиска 1 подходящей строки необходимо прочитать 100 строк.
Если вы выполнили этот запрос до завершения (что может занять очень много времени), вы, скорее всего, обнаружите, что из сортировки нужно было прочитать более 93/94 строк, чтобы в итоге получить 100 строк. В худшем случае 100-й ряд будет найден с использованием последнего ряда из сортировки. Если предположить, что оценка оптимизатора на узле 4 верна, это означает, что сканирование выполняется на узле 5 38 284 раза, что в общей сложности составляет около 15 миллиардов строк. Это может быть больше, если оценки сканирования также отключены.
Этот план выполнения также включает предупреждение об отсутствующем индексе:
Оптимизатор предупреждает вас о том, что добавление индекса в таблицу повысит производительность.
План без лишних столбцов
По сути, это тот же план, что и предыдущий, с добавлением катушки индекса на узле 6 и фильтра на узле 5. Важными отличиями являются:
Operation_Type
иStart_Time
,Id
в качестве неключевого столбца.Operation_Type
,Start_Time
,Finish_Time
иId
из проверки на узле 4 передаются на внутренней стороне ветвь , как внешние ссылки.Operation_Type
совпадает с текущим внешним значением ссылки, иStart_Time
находится в диапазоне, определенном внешнимиStart_Time
иFinish_Time
внешними ссылками.Id
значения в пуле индексов на предмет неравенства с текущим внешним эталонным значениемId
.Ключевые улучшения:
Operation_Type
,Start_Time
) сId
включенным столбцом позволяет объединять вложенные циклы индекса. Индекс используется для поиска совпадающих строк на каждой итерации, а не для сканирования всей таблицы каждый раз.Как и прежде, оптимизатор включает предупреждение об отсутствующем индексе:
Вывод
План без дополнительных столбцов быстрее, потому что оптимизатор решил создать временный индекс для вас.
План с дополнительными столбцами сделает создание временного индекса более дорогим.
[Parameters
Колонка] естьnvarchar(2000)
, который хотел бы добавить до 4000 байт для каждой строки индекса. Дополнительные затраты достаточны, чтобы убедить оптимизатора в том, что построение временного индекса при каждом выполнении не окупится.В обоих случаях оптимизатор предупреждает, что постоянный индекс будет лучшим решением. Идеальная структура индекса зависит от вашей более широкой рабочей нагрузки. Для этого конкретного запроса предложенные индексы являются разумной отправной точкой, но вы должны понимать преимущества и затраты.
Рекомендация
Широкий диапазон возможных индексов будет полезным для этого запроса. Важным выводом является то, что нужен какой-то некластеризованный индекс. Исходя из представленной информации, разумным показателем на мой взгляд будет:
Я также хотел бы немного лучше организовать запрос и отложить поиск широких
[Parameters]
столбцов в кластеризованном индексе до тех пор, пока не будут найдены первые 100 строк (используяId
в качестве ключа):Если
[Parameters]
столбцы не нужны, запрос можно упростить до:FORCESEEK
Подсказка , чтобы помочь гарантировать , что оптимизатор выбирает индексированные вложенные циклы планирование (есть стоимостный соблазн для оптимизатора , чтобы выбрать хеш или (много-много) слиянием в противном случае, который , как правило , не хорошо работать с этим типом запрос на практике. Оба результата приводят к большим остаткам: много элементов в каждом сегменте в случае хэша и много перемоток для объединения).альтернатива
Если бы запрос (включая его конкретные значения) был особенно важен для производительности чтения, я бы рассмотрел два отфильтрованных индекса:
Для запроса, который не нуждается в
[Parameters]
столбце, предполагаемый план с использованием отфильтрованных индексов:Сканирование индекса автоматически возвращает все подходящие строки без оценки каких-либо дополнительных предикатов. Для каждой итерации соединения с вложенными циклами индекса поиск индекса выполняет две операции поиска:
Operation_Type
иState
= 3, а затем ищет диапазонStart_Time
значений, остаточный предикатId
неравенства.Operation_Type
иState
= 4, затем ищет диапазонStart_Time
значений, остаточный предикатId
неравенства.Там, где необходим
[Parameters]
столбец, план запроса просто добавляет не более 100 одиночных поисков для каждой таблицы:В заключение, вам следует рассмотреть возможность использования встроенных стандартных целочисленных типов вместо того,
numeric
где это применимо.источник
Пожалуйста, создайте следующий индекс:
источник