#sql-server-2008 #tsql
#sql-server-2008 #tsql
Вопрос:
Допустим, у меня есть хранимая процедура, которая будет выполнять сложную логику при изменении параметра @ComplicatedSearch = 1
. Когда значение равно 1, я заполняю переменную table @ValidIds
допустимыми строками, которые может вернуть эта процедура. Когда значение равно 0, логика обходится, и нам не нужно отфильтровывать возвращаемые строки.
Итак, следуя этой логике, я получаю инструкцию, подобную so:
SELECT
m.*
,a.*
FROM
MyTable m
INNER JOIN AdditionalInfoTable a
ON m.Id = a.MyTableId
WHERE
(@ComplicatedSearch = 0 OR EXISTS(SELECT * FROM @ValidIds WHERE Id = m.Id))
Это работает нормально; однако я считаю, что было бы эффективнее присоединить MyTable к @ValidIds, когда это применимо, в отличие от использования EXISTS()
, особенно когда MyTable содержит большое количество строк.
Есть ли какой-либо способ сделать что-то вроде того, что я привел ниже, без написания нескольких запросов? (фактический запрос очень большой, поэтому наличие нескольких версий с объединениями и без них было бы не идеально)
SELECT
m.*
,a.*
FROM
MyTable m
ONLY DO THIS IF ComplicatedSearch = 1 PLEASE: INNER JOIN @ValidIds v
ON m.Id = v.Id
INNER JOIN AdditionalInfoTable a
ON m.Id = a.MyTableId
Комментарии:
1. Ваше убеждение, что
JOIN
было бы более производительным, чемEXISTS
, неуместно.EXISTS
очень эффективно реализовано в SQL Server как полусоединение и может использовать любой из доступных типов соединения.2. @MartinSmith: Я думаю, чего я пытаюсь избежать, так это полного сканирования таблицы в MyTable; Я надеялся, что объединение позволит более эффективно захватывать строки
3. Не уверен точно, какой план вы получили бы с этим, не настроив тест на этом этапе. Вы могли бы попробовать добавить,
OPTION (RECOMPILE)
чтобы учитывалось как значение@ComplicatedSearch
, так и мощность табличной переменной. Также убедитесь, что переменная table объявлена сId primary key
, чтобы получить индекс, созданный для этого столбца.
Ответ №1:
Другой вариант:
SELECT
m.*
,a.*
FROM MyTable m
INNER JOIN @ValidIds v
ON m.Id = case
when @ComplicatedSearch = 1 then v.Id -- Filter rows
else m.Id -- Select all rows
end
INNER JOIN AdditionalInfoTable a
ON m.Id = a.MyTableId
Вам нужно будет выполнить тестирование производительности, чтобы убедиться, что оно достаточно эффективно. Некоторые быстрые тесты показали, что (по моим данным) был сгенерирован один и тот же план запроса независимо от того, был ли первый вызов для complex или не complex.
«Раздвоенный» подход (отдельные процедуры) должен быть наиболее эффективным. Однако поддержка одного и того же кода с незначительными изменениями в двух разных местах может быть серьезной проблемой, особенно когда вам приходится добавлять последующие изменения ко всем «экземплярам» этого кода. И если общий размер данных (например, общая производительность) не слишком велик, подход «один размер в основном подходит всем» может быть наиболее эффективным.
Комментарии:
1. Идеально! Единственное, на что следует обратить внимание, это то, что мне нужно будет вставить фиктивную строку в эту таблицу, когда @ComplicatedSearch равно 0, чтобы предотвратить уничтожение всего этим внутренним объединением
2. Ого! Я пропустил это! (Я просто жестко запрограммировал некоторые значения при тестировании сгенерированных планов запросов.)
3. Не уверен, почему это принято, поскольку, похоже, это не работает. В случае, когда вы присоединяетесь обратно к той же таблице, SQL server все равно должен включать строки для @validIds, поскольку эта таблица была присоединена. Результатом является значительно большее количество строк (и намного более дорогой и медленный запуск запроса), чем предполагалось.
Ответ №2:
Если вам нужна эффективность, вы должны отметить, что хранимые процедуры вычислят план запроса при первом запуске, затем кэшируют его и используют тот же самый в дальнейшем. В данном случае это означает, что он выберет использование @ValidIds в зависимости от первого значения @ComplicatedSearch
Таким образом, я бы написал процедуру, больше похожую
if @ComplicatedSearch = 1
exec simple_search
else
exec complex_search
Где simple_search включает ваш первый запрос, а complex_search также присоединяется к @ValidIds
Да, вы получаете запрос дважды, но чтобы справиться с этим, я бы создал представление
create view helper as
begin
SELECT
m.*
,a.*
FROM
MyTable m
INNER JOIN AdditionalInfoTable a
ON m.Id = a.MyTableId
end
и затем простой выбор из этого представления и сложные соединения с @ValidIds