Підготовка партії SQL окремо від виконання підготовленої партії SQL - це безкорисна конструкція для SQL Server з урахуванням того, як кешуються плани виконання. Розмежування етапів підготовки (розбору, прив'язки будь-яких параметрів та компіляції) та виконання має сенс лише тоді, коли немає кешування. Метою є економія часу, витраченого на розбір та компіляцію шляхом повторного використання існуючого плану, і саме це робить кеш внутрішнього плану. Але не всі RDBMS виконують цей рівень кешування (зрозуміло, що існує в цих функціях), і тому від клієнтського коду залишається запитувати, щоб план був "кешований", і таким чином зберегти цей ідентифікатор кешу, а потім повторно використовувати це. Це додаткове навантаження на клієнтський код (і програміст пам'ятати, щоб це зробити і правильно), що зайве. Фактично, сторінка 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()
виклику реєструється як подія "Підготувати SQL", а наступні .Execute___()
виклики реєструються як події "Exec Prepared SQL".
Код тесту
Він завантажений у PasteBin за адресою: http://pastebin.com/Yc2Tfvup . Код створює додаток .NET / C # Console, який призначений для роботи під час запуску трафіку SQL Server Profiler (або сеансу розширених подій). Він робить паузи після кожного кроку, щоб було зрозуміло, які твердження мають особливий ефект.
ОНОВЛЕННЯ
Знайдено більше інформації та невелику потенційну причину, щоб уникнути дзвінків SqlCommand.Prepare()
. Одне, що я помітив під час тестування, і що слід зазначити для тих, хто працює з тестовою програмою Console App: ніколи не робиться явного дзвінка 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()
), враховуючи, що це є основним об'єктом Орієнтоване програмування, яке ви, ймовірно, вже робите в коді програми ;-).