Подготовка пакета SQL отдельно от выполнения подготовленного пакета SQL - это эффективная конструкция **бесполезно для SQL Server, учитывая, как планы выполнения кэшируются. Разделение этапов подготовки (разбора, привязки любых параметров и компиляции) и выполнения имеет смысл только тогда, когда нет кэширования. Цель состоит в том, чтобы сэкономить время, затрачиваемое на синтаксический анализ и компиляцию, путем повторного использования существующего плана, и именно это делает внутренний кэш планов. Но не все РСУБД выполняют этот уровень кэширования (очевидно, из-за наличия этих функций), и поэтому клиентский код должен запросить, чтобы план был «кэширован», и таким образом сохранить этот идентификатор кэша, а затем повторно использовать Это. Это дополнительная нагрузка на клиентский код (и программист должен помнить, чтобы сделать это и сделать это правильно), который не нужен. На самом деле, страница MSDN для метода IDbCommand.Prepare () гласит:
Сервер автоматически кэширует планы для повторного использования по мере необходимости; следовательно, нет необходимости вызывать этот метод непосредственно в вашем клиентском приложении.
Следует отметить , что, насколько мои тестирования показывает (что соответствует тому , что вы показываете в вопросе), называя SqlCommand.Prepare () , это не делать «подготовить» -только операцию: она вызывает sp_prepexec , который готовит и выполняет SQL ; он не вызывает sp_prepare, который только анализирует и компилирует (и не принимает никаких значений параметров, только их имена и типы данных). Следовательно, не может быть никакого преимущества «прекомпиляции» вызова, SqlCommand.Prepare
так как он выполняет немедленное выполнение (предполагая, что цель - отложить выполнение). ОДНАКО , есть интересная потенциальная незначительная выгода от звонка SqlCommand.Prepare()
, даже если он звонит sp_prepexec
вместо sp_prepare
. Пожалуйста, смотрите примечание (** ) внизу для деталей.
Мои тесты показывают , что даже тогда , когда SqlCommand.Prepare()
вызывается (и обратите внимание , что этот призыв никак не выдавать любые команды на SQL Server), то ExecuteNonQuery
или ExecuteReader
что следует полностью выполнена, и если это ExecuteReader, он возвращает строки. Тем не менее, SQL Server Profiler действительно показывает, что первый SqlCommand.Execute______()
вызов после SqlCommand.Prepare()
вызова регистрируется как событие «Prepare SQL», а последующие .Execute___()
вызовы регистрируются как события «Exec Prepared SQL».
Тестовый код
Он был загружен в PasteBin по адресу: http://pastebin.com/Yc2Tfvup . Код создает консольное приложение .NET / C #, которое предназначено для запуска во время трассировки SQL Server Profiler (или сеанса расширенных событий). Он делает паузу после каждого шага, чтобы было ясно, какие операторы имеют определенный эффект.
ОБНОВИТЬ
Нашли больше информации и небольшую потенциальную причину, чтобы не звонить SqlCommand.Prepare()
. Одна вещь, которую я заметил в своем тестировании, и что следует отметить для тех, кто работает с этим тестовым консольным приложением: явный вызов никогда не выполняется sp_unprepare
. Я немного покопался и нашел следующий пост на форумах MSDN:
SqlCommand - Готовить или не готовить?
Принятый ответ содержит кучу информации, но существенными моментами являются:
- ** То, что готовит на самом деле, экономит вас - это, прежде всего, время, необходимое для передачи строки запроса по сети.
- Подготовленный запрос не гарантирует сохранность кэшированного плана и не быстрее, чем специальный запрос, когда он достигает сервера.
- RPC (CommandType.StoredProcedure) ничего не получают от подготовки.
- На сервере подготовленный дескриптор - это в основном индекс в карте памяти, содержащей TSQL для выполнения.
- Карта хранится отдельно для каждого соединения, кросс-соединение не возможно.
- Сброс, отправленный, когда клиент повторно использует соединение из пула, очищает карту.
Я также нашел следующий пост от команды SQLCAT, который, похоже, связан, но может быть просто проблемой, специфичной для ODBC, тогда как SqlClient корректно очищается после удаления SqlConnection. Трудно сказать, поскольку в посте SQLCAT не упоминаются какие-либо дополнительные тесты, которые могут помочь доказать это как причину, например, очистка пула соединений и т. Д.
Следите за подготовленными операторами SQL
ВЫВОД
При условии:
- единственное реальное преимущество звонка
SqlCommand.Prepare()
заключается в том, что вам не нужно повторно отправлять текст запроса по сети,
- вызов
sp_prepare
и sp_prepexec
сохранение текста запроса как части памяти соединения (т. е. памяти SQL Server)
Я рекомендовал бы против вызова , SqlCommand.Prepare()
поскольку единственным потенциальным преимуществом является сохранение сетевых пакетов в то время как нижняя сторона принимает больше памяти сервера. Хотя объем потребляемой памяти, вероятно, очень мал в большинстве случаев, пропускная способность сети редко бывает проблемой, так как большинство серверов БД напрямую подключаются к серверам приложений с минимальной скоростью 10 мегабит (в наши дни, скорее всего, 100 мегабит или гигабит) ( а некоторые даже на одной коробке ;-). Это и память почти всегда более дефицитный ресурс, чем пропускная способность сети.
Я полагаю, что для любого, кто пишет программное обеспечение, которое может подключаться к нескольким базам данных, удобство стандартного интерфейса может изменить этот анализ затрат / выгод. Но даже в этом случае я должен верить, что все еще достаточно просто абстрагировать «стандартный» интерфейс БД, который учитывает разных провайдеров (и, следовательно, различия между ними, такие как отсутствие необходимости вызова Prepare()
), учитывая, что это базовый объект. Ориентированное программирование, которое вы, вероятно, уже делаете в коде своего приложения ;-).