Я наткнулся на код разработчика, где метод SqlCommand.Prepare () (см. MSDN) широко используется перед выполнением SQL-запросов. И мне интересно, в чем выгода?
Образец:
command.Prepare();
command.ExecuteNonQuery();
//...
command.Parameters[0].Value = 20;
command.ExecuteNonQuery();
Я немного поиграл и проследил. Выполнение команды после вызова Prepare()
метода заставляет Sql Server выполнить следующий оператор:
declare @p1 int
set @p1=1
exec sp_prepexec @p1 output,N'@id int,@desc text',N'INSERT INTO dbo.testtable (id) VALUES (@id)',@id=20'
select @p1
После этого, когда параметр получает свое значение и SqlCommand.ExecuteNonQuery()
вызывается, на Sql-сервере выполняется следующее:
exec sp_execute 1,@id=20
Для меня это выглядит так, будто это заявление компилируется, как только Prepare()
выполняется. Интересно, в чем выгода? Означает ли это, что он помещается в кэш плана и может использоваться повторно, как только будет выполнен последний запрос с требуемыми значениями параметров?
Я выяснил (и задокументировал это в другом вопросе ), что SqlCommands, которые выполняются с SqlParameters, всегда заключены в sp_executesql
вызовы процедур. Это позволяет Sql Server сохранять и повторно использовать планы независимо от значений параметров.
В связи с этим мне интересно, является ли этот prepare()
метод бесполезным или устаревшим или я что-то здесь упускаю?
Ответы:
Подготовка пакета 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 - Готовить или не готовить?
Принятый ответ содержит кучу информации, но существенными моментами являются:
Я также нашел следующий пост от команды SQLCAT, который, похоже, связан, но может быть просто проблемой, специфичной для ODBC, тогда как SqlClient корректно очищается после удаления SqlConnection. Трудно сказать, поскольку в посте SQLCAT не упоминаются какие-либо дополнительные тесты, которые могут помочь доказать это как причину, например, очистка пула соединений и т. Д.
Следите за подготовленными операторами SQL
ВЫВОД
При условии:
SqlCommand.Prepare()
заключается в том, что вам не нужно повторно отправлять текст запроса по сети,sp_prepare
иsp_prepexec
сохранение текста запроса как части памяти соединения (т. е. памяти SQL Server)Я рекомендовал бы против вызова ,
SqlCommand.Prepare()
поскольку единственным потенциальным преимуществом является сохранение сетевых пакетов в то время как нижняя сторона принимает больше памяти сервера. Хотя объем потребляемой памяти, вероятно, очень мал в большинстве случаев, пропускная способность сети редко бывает проблемой, так как большинство серверов БД напрямую подключаются к серверам приложений с минимальной скоростью 10 мегабит (в наши дни, скорее всего, 100 мегабит или гигабит) ( а некоторые даже на одной коробке ;-). Это и память почти всегда более дефицитный ресурс, чем пропускная способность сети.Я полагаю, что для любого, кто пишет программное обеспечение, которое может подключаться к нескольким базам данных, удобство стандартного интерфейса может изменить этот анализ затрат / выгод. Но даже в этом случае я должен верить, что все еще достаточно просто абстрагировать «стандартный» интерфейс БД, который учитывает разных провайдеров (и, следовательно, различия между ними, такие как отсутствие необходимости вызова
Prepare()
), учитывая, что это базовый объект. Ориентированное программирование, которое вы, вероятно, уже делаете в коде своего приложения ;-).источник
sp_prepare
в вызовsp_executesql
. Для шага 10, да, я вчера провёл больше тестов и видел несколько случаев с разными ручками дляsp_prepare
, но всё равно никогда не было одинаковым дляsp_executesql
. Я проверю еще одну вещь и обновлю свой ответ.sp_executesql
не может или не может. Но независимо от того, время разбора все еще там,sp_prepare
если план был удален из кеша, поэтому я удалю эту часть моего ответа (интересно, но не имеет значения). В любом случае, эта часть была более интересной, поскольку она не затрагивала ваш прямой вопрос. Все остальное все еще применяется.Я бы сказал, что метод Prepare имеет ограниченную ценность SqlCommand world, а не то, что он совершенно бесполезен. Одним из преимуществ является то, что Prepare является частью интерфейса IDbCommand, который реализует SqlCommand. Это позволяет тому же коду запускаться с другими поставщиками СУБД (которые могут требовать Prepare) без условной логики, чтобы определить, следует ли вызывать Prepare или нет.
Prepare не имеет значения для провайдера .NET для SQL Server, кроме случаев использования AFAIK.
источник