SQL Server кешира плановете за изпълнение за ad-hoc заявки, така че (като се отстъпи времето, необходимо за първото извикване) двата подхода ще бъдат идентични по отношение на скоростта.
Като цяло използването на съхранени процедури означава вземане на част от кода, необходим на вашето приложение (заявките T-SQL) и поставянето му на място, което не е под контрол на източника (то може е, но обикновено не е ) и където може да бъде променено от други без ваше знание.
Разполагането на запитванията на централно място като това може да бъде нещо добро, в зависимост от това колко различни приложения се нуждаят от достъп до данните, които представляват. Обикновено смятам, че е много по-лесно да запазя заявките, използвани от приложение, резидентни в самия код на приложението.
В средата на 90-те години на миналия век общоприетата мъдрост казваше, че съхранените процедури в SQL Server са начинът, по който трябва да се действа в критични за производителността ситуации, и по това време определено бяха. Причините зад това CW обаче не са валидни от дълго време.
Актуализация: Също така, често в дебатите относно жизнеспособността на запомнените процедури, необходимостта от предотвратяване на SQL инжектиране се извиква в защита на procs. Със сигурност никой с здрав ум не смята, че сглобяването на ad hoc заявки чрез конкатенация на низове е правилното нещо (въпреки че това ще ви изложи на атака с инжектиране на SQL само ако конкатенирате потребителско въвеждане ). Очевидно ad hoc заявките трябва да бъдат параметризирани, не само за да се предотврати чудовището под леглото на атака с инжектиране на sql, но и просто за да улесни живота ви като програмист като цяло (освен ако не ви харесва да разберете кога да използвате единичен кавички около вашите ценности).
Актуализация 2: Направих повече изследвания. Въз основа на тази бяла книга на MSDN , изглежда, че отговорът зависи от това какво точно имате предвид под „ad-hoc“ с вашите запитвания. Например проста заявка като тази:
SELECT ID, DESC FROM tblSTUFF WHERE ITEM_COUNT > 5
... ще има кеширан план за изпълнение. Освен това, тъй като заявката не съдържа определени дисквалифициращи елементи (като почти всичко, различно от обикновен SELECT от една таблица), SQL Server всъщност ще "автоматично параметризира" заявката и ще замени литералната константа "5" с параметър и ще кешира планът за изпълнение за параметризираната версия. Това означава, че ако след това изпълните това ad hoc заявка:
SELECT ID, DESC FROM tblSTUFF WHERE ITEM_COUNT > 23
... ще може да използва кеширания план за изпълнение.
За съжаление, списъкът с дисквалифициращи елементи на заявка за автоматично параметризиране е дълъг (например, забравете за използването на DISTINCT
, TOP
, UNION
, GROUP BY
, OR
и т.н.), така че наистина не можете да разчитате на това за ефективност.
Ако имате „супер сложна“ заявка, която няма да бъде автоматично параметризирана, като:
SELECT ID, DESC FROM tblSTUFF WHERE ITEM_COUNT > 5 OR ITEM_COUNT < 23
... тя все още ще бъде кеширана от точния текст на заявката, така че ако вашето приложение извиква тази заявка с едни и същи буквални „твърдо кодирани“ стойности многократно, всяка заявка след първата ще използва повторно кеширания план за изпълнение (и по този начин да бъде толкова бърз, колкото съхранена процедура).
Ако литералните стойности се променят (въз основа на действия на потребителя, например като филтриране или сортиране на преглеждани данни), тогава заявките няма да се възползват от кеширането (освен понякога, когато случайно съответстват точно на скорошна заявка).
Начинът да се възползвате от кеширането с "ad-hoc" заявки е да ги параметризирате. Създаване на заявка в движение в C# по този начин:
int itemCount = 5;
string query = "DELETE FROM tblSTUFF WHERE ITEM_COUNT > " +
itemCount.ToString();
е неправилно. Правилният начин (използвайки ADO.Net) би бил нещо подобно:
using (SqlConnection conn = new SqlConnection(connStr))
{
SqlCommand com = new SqlCommand(conn);
com.CommandType = CommandType.Text;
com.CommandText =
"DELETE FROM tblSTUFF WHERE ITEM_COUNT > @ITEM_COUNT";
int itemCount = 5;
com.Parameters.AddWithValue("@ITEM_COUNT", itemCount);
com.Prepare();
com.ExecuteNonQuery();
}
Заявката не съдържа литерали и вече е напълно параметризирана, така че следващите заявки, използващи идентичен параметризиран израз, ще използват кеширания план (дори ако се извикат с различни стойности на параметри). Обърнете внимание, че кодът тук е практически същият като кода, който бихте използвали за извикване на съхранена процедура така или иначе (единствената разлика е CommandType и CommandText), така че донякъде се свежда до това къде искате текстът на тази заявка да „живее“ " (в кода на вашето приложение или в съхранена процедура).
И накрая, ако под „ad-hoc“ заявки имате предвид, че динамично конструирате заявки с различни колони, таблици, параметри за филтриране и какво ли още не, като може би тези:
SELECT ID, DESC FROM tblSTUFF WHERE ITEM_COUNT > 5
SELECT ID, FIRSTNAME, LASTNAME FROM tblPEEPS
WHERE AGE >= 18 AND LASTNAME LIKE '%What the`
SELECT ID, FIRSTNAME, LASTNAME FROM tblPEEPS
WHERE AGE >= 18 AND LASTNAME LIKE '%What the`
ORDER BY LASTNAME DESC
... тогава почти не можете направете това със съхранени процедури (без EXEC
хак, за който не бива да се говори в учтиво общество), така че въпросът е спорен.
Актуализация 3: Ето единственото наистина добро свързано с производителността причина (за която се сещам, така или иначе) за използване на съхранена процедура. Ако вашата заявка е продължителна, при която процесът на компилиране на плана за изпълнение отнема значително повече време от действителното изпълнение и заявката се извиква само рядко (като месечен отчет, например), тогава поставянето й в съхранена процедура може накарайте SQL Server да пази компилирания план в кеша достатъчно дълго, за да може да го има следващия месец. Все пак ме чуди дали това е вярно или не.