Некоторые вещи в вашем запросе и дизайне БД обязательно должны быть исправлены.
Прежде всего, все таблицы должны быть правильно проиндексированы для достижения максимальной производительности. Вы писали: Самая большая ничья, которую я вижу, что я не могу контролировать, это то, что таблица "item_xref" не проиндексирована . Почему вы не контролируете это? Кто контролирует? Чтобы запрос выполнялся в лучшем случае, обязательно, чтобы все таблицы имели минимальное количество индексов, по крайней мере, для столбцов, участвующих в условиях соединения (например, «объединить элемент как i с (nolock) в ix.COMPONENT_ITEM_ID = i.ITEM_ID», COMPONENT_ITEM_ID) должны быть проиндексированы, если это не кластеризованный индекс таблицы).
Я настоятельно рекомендую вам найти того, кто контролирует дизайн таблиц для реализации необходимых индексов .
Теперь я буду работать последовательной итерацией в поисках оптимальных результатов. Для некоторых итераций могут потребоваться разрешения на изменение таблицы или создание индексов, поэтому, если у вас их нет, найдите того, кто имеет.
1-я итерация: исключить вложенный запрос
Что касается самого вложенного запроса, так как он охватывает ту же таблицу основного запроса (item_xref), вы можете избежать его, избавившись от вложенного запроса и предложения IN, что не является одной из самых производительных операций в Sql Сервер. Вы можете преобразовать его в JOIN или просто применить условия непосредственно к основному запросу (что я предпочитаю). Ваш запрос становится следующим:
select
i.ITEM_ID as "Components of Target",
m.DESCRIPTION as "Component Type",
ix.LAST_UPDATE as "Time added",
ix.LAST_USER as "Added by"
from item_xref as ix with (nolock)
join item as i with (nolock) on ix.COMPONENT_ITEM_ID = i.ITEM_ID
join model as m with (nolock) on i.MODEL_ID = m.MODEL_ID
where ix.item_ID = @variable
and ix.COMPONENT_ITEM_ID not like '1T%'
and ix.COMPONENT_ITEM_ID not like 'T4%'
and ix.LAST_USER not like '%IMM%'
and ix.LAST_USER not like '%HST%'
2-я итерация: лайки не производительны
В вашем запросе есть два вида операторов LIKE: "Начинается с" like ( например, ix.COMPONENT_ITEM_ID не похож на «1T%» и «Contains» (например, ix.LAST_USER не похож на «% IMM%»). И то, и другое неэффективно, но последнее является одной из самых неэффективных вещей, которые вы можете сделать на Sql Сервере :) Когда вам понадобится использовать оператор LIKE, спросите себя, что вы пытаетесь выполнить sh. Мне кажется, что здесь вы в основном кластеризуете ваши записи в определенных c группах. С
and ix.COMPONENT_ITEM_ID not like '1T%'
и
and ix.COMPONENT_ITEM_ID not like 'T4%'
Вы в основном говорите: «Я не хочу, чтобы записи были в группе« Начинается с 1Т »или« Начинается с Т4 ». есть категории компонентов, обозначенные первыми двумя буквами их COMPONENT_ITEM_ID. Вы знаете, что эти компоненты как-то различаются, и вам следует передать эти знания в свой дизайн базы данных и в клиентское приложение. Но давайте предположим, что вы не можете создать таблицу item_category и создать внешний ключ. Вы все еще можете сделать что-то, если у вас есть разрешения на изменение таблицы и создание индекса: вы можете использовать мощь вычисляемых столбцов. Вычисляемый столбец не является физическим столбцом в таблице, но вычисляется с использованием правила для других столбцов каждый раз, когда вычисляемый столбец включается в операцию чтения (например, в «SELECT MyComputedColumn»). Интересный факт заключается в том, что если вы создаете индекс для вычисляемого столбца, индекс фактически фактически сохраняется и обновляется при каждом ВСТАВИТЬ или ОБНОВИТЬ е стол. Мое предложение состоит в том, чтобы создать вычисляемый столбец в таблице item_xref и создать его индекс. Давайте рассмотрим некоторый код:
ALTER TABLE dbo.item_xref ADD
ITEM_CATEGORY AS LEFT(COMPONENT_ITEM_ID, 2)
GO
CREATE NONCLUSTERED INDEX [IX_ItemCategory] ON [dbo].[item_xref]
(
[ITEM_CATEGORY] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF,
SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF,
ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON,
OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF)
«Содержит» подобные операторы более сложны, но применима та же концепция:
ALTER TABLE dbo.item_xref ADD
LAST_USER_GROUP AS CASE
WHEN LAST_USER LIKE '%IMM%' THEN 'IMM'
WHEN LAST_USER LIKE '%HST%' THEN 'HST'
-- add other cases if needed
ELSE '---'
END
GO
CREATE NONCLUSTERED INDEX [IX_LastUserGroup] ON [dbo].[item_xref]
(
[LAST_USER_GROUP] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF,
SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF,
ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON,
OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF)
Выполнение запроса может:
select
i.ITEM_ID as "Components of Target",
m.DESCRIPTION as "Component Type",
ix.LAST_UPDATE as "Time added",
ix.LAST_USER as "Added by"
from item_xref as ix with (nolock)
join item as i with (nolock) on ix.COMPONENT_ITEM_ID = i.ITEM_ID
join model as m with (nolock) on i.MODEL_ID = m.MODEL_ID
where ix.item_ID = @variable
and ix.COMPONENT_ITEM_ID <> '1T'
and ix.COMPONENT_ITEM_ID <> 'T4'
and ix.LAST_USER <> 'IMM'
and ix.LAST_USER <> 'HST'
Это должно быть значительно быстрее, чем ваш исходный запрос (я предлагаю вам проверить с помощью профилировщика и проверить план выполнения). Последнее замечание: остерегайтесь подсказки WITH (NOLOCK), она эквивалентна READ_UNCOMMITTED, что приводит к грязным или дублированным результатам. Как только запрос выполняется достаточно быстро, он может вам больше не понадобиться.
Возможна другая итерация
Если вам не нужно запрашивать оперативные данные, но достаточно использовать устаревшие данные указанного c периода, можно запланировать создание снимка базы данных каждый {период} и запросить снимок вместо действующей базы данных. Таким образом, вы не будете страдать от какой-либо блокировки от операций записи. Это полезно при составлении отчетов по расписанию, обычно, когда вам приходится создавать отчет каждый день или неделю: сначала вы создаете / обновляете снимок базы данных, а затем запрашиваете его. Имейте в виду, что: моментальный снимок будет занимать то же пространство, что и файлы базы данных главной базы данных, поэтому проверьте место на диске!