Как предупреждение, я работаю с SQL-запросом, сгенерированным из структуры сущностей, хотя структура сущностей не имеет отношения к этому вопросу.
Некоторый контекст:
Я пытаюсь извлечь определенные записи из пакета из 4000 объектов C # и выполнить обновление или вставку в них. У меня нет первичного ключа записей, поскольку объекты поступают из API, поэтому мне нужно использовать уникальный набор столбцов для получения правильной записи.
(Упрощенные) запросы и планы их выполнения:
Параметризованный запрос (параметры объявлены со значением, установленным в 1 для демонстрационных целей)
SELECT [x].[Id]
FROM [Gradebook].[AssignmentScore] AS [x]
WHERE
( ( ((([x].[CourseSectionAssignment_Id] = @__CSA_1) AND ([x].[Student_Id] = @__S_ID_1)) AND ([x].[AssessmentGBID] = @__A_GBID_1))
OR ((([x].[CourseSectionAssignment_Id] = @__CSA_2) AND ([x].[Student_Id] = @__S_ID_2)) AND ([x].[AssessmentGBID] = @__A_GBID_2)) )
OR ((([x].[CourseSectionAssignment_Id] = @__CSA_3) AND ([x].[Student_Id] = @__S_ID_3)) AND ([x].[AssessmentGBID] = @__A_GBID_3)) )
И это (неблагоприятный) план выполнения:
![Parameterized query execution plan](https://i.stack.imgur.com/gVED3.jpg)
Запрос литеральных значений:
SELECT [x].[Id]
FROM [Gradebook].[AssignmentScore] AS [x]
WHERE
( ( ((([x].[CourseSectionAssignment_Id] = 1) AND ([x].[Student_Id] = 2)) AND ([x].[AssessmentGBID] = 3))
OR ((([x].[CourseSectionAssignment_Id] = 4) AND ([x].[Student_Id] = 5)) AND ([x].[AssessmentGBID] = 6)) )
OR ((([x].[CourseSectionAssignment_Id] = 7) AND ([x].[Student_Id] = 8)) AND ([x].[AssessmentGBID] = 9)) )
И это (выгодный) план выполнения:
![Execution plan of query with literal values](https://i.stack.imgur.com/07hxc.jpg)
Я знаю, почему, или, по крайней мере, я верю, что знаю, почему планы выполнения отличаются. Это связано с тем, что оптимизатор не знает, будут ли параметры иметь значение NULL, и должен оптимизировать этот случай. Тестирование литерального запроса с NULL создает неблагоприятный план выполнения. (Почему анализатор параметров не видит, что значения не равны NULL, и не создает лучший план выполнения?)
В настоящее время в коде C # я вручную использую деревья выражений, чтобы заменить свойства объекта константами выражений, чтобы сгенерированный запрос был литеральным запросом. Насколько я знаю, литералы заставляют SQL-сервер каждый раз генерировать новый план выполнения, что не очень хорошо.
Мне бы хотелось, чтобы параметризованный запрос генерировал благоприятный план выполнения. Пока что единственный ответ, который я нашел, - это использование подсказки OPTION(RECOMPILE)
, что не совсем то, что я хочу, поскольку заставляет пересоздать план выполнения.
Как я могу каждый раз использовать один и тот же выгодный план выполнения с параметризованным запросом?