SQL Server memorizza nella cache i piani di esecuzione per le query ad hoc, quindi (scontando il tempo impiegato dalla prima chiamata) i due approcci saranno identici in termini di velocità.
In generale, l'uso delle stored procedure significa prendere una parte del codice necessaria alla tua applicazione (le query T-SQL) e metterla in una posizione che non è sotto il controllo del codice sorgente (può può essere, ma di solito non è ) e dove può essere modificato da altri a tua insaputa.
Avere le query in un posto centrale come questo può essere una buona cosa, a seconda di quante diverse applicazioni hanno bisogno di accedere ai dati che rappresentano. In genere trovo molto più semplice mantenere le query utilizzate da un'applicazione residenti nel codice dell'applicazione stessa.
A metà degli anni '90, l'opinione comune affermava che le stored procedure in SQL Server erano la strada da percorrere in situazioni critiche per le prestazioni, e all'epoca lo erano decisamente. Le ragioni alla base di questo CW non sono valide da molto tempo, tuttavia.
Aggiornamento: Inoltre, spesso nei dibattiti sulla fattibilità delle procedure archiviate, viene invocata la necessità di impedire l'iniezione di SQL a difesa dei processi. Sicuramente, nessuno sano di mente pensa che assemblare query ad hoc tramite la concatenazione di stringhe sia la cosa corretta da fare (sebbene questo ti esporrà a un attacco SQL injection solo se stai concatenando input utente ). Ovviamente le query ad hoc dovrebbero essere parametrizzate, non solo per prevenire il monster-under-the-bed di un attacco sql injection, ma anche solo per semplificarti la vita da programmatore in genere (a meno che non ti piaccia dover capire quando usare single virgolette intorno ai tuoi valori).
Aggiornamento 2: Ho fatto più ricerche. Basato su questo white paper MSDN , sembra che la risposta dipenda esattamente da cosa intendi per "ad-hoc" con le tue domande. Ad esempio, una semplice query come questa:
SELECT ID, DESC FROM tblSTUFF WHERE ITEM_COUNT > 5
... farà avere il suo piano di esecuzione memorizzato nella cache. Inoltre, poiché la query non contiene determinati elementi di squalifica (come quasi qualsiasi cosa diversa da un semplice SELECT da una tabella), SQL Server in realtà "parametrizzerà automaticamente" la query e sostituirà la costante letterale "5" con un parametro e inserirà nella cache il piano di esecuzione per la versione parametrizzata. Ciò significa che se poi esegui questo query ad hoc:
SELECT ID, DESC FROM tblSTUFF WHERE ITEM_COUNT > 23
... sarà in grado di utilizzare il piano di esecuzione memorizzato nella cache.
Sfortunatamente, l'elenco degli elementi di query squalifica per la parametrizzazione automatica è lungo (ad esempio, dimentica di usare DISTINCT
, TOP
, UNION
, GROUP BY
, OR
ecc.), quindi non puoi davvero contare su questo per le prestazioni.
Se hai una query "super complessa" che non verrà parametrizzata automaticamente, ad esempio:
SELECT ID, DESC FROM tblSTUFF WHERE ITEM_COUNT > 5 OR ITEM_COUNT < 23
... verrà comunque memorizzato nella cache dal testo esatto della query, quindi se la tua applicazione chiama ripetutamente questa query con gli stessi valori letterali "codificati", ogni query successiva alla prima riutilizzerà il piano di esecuzione memorizzato nella cache (e quindi essere veloce come un processo memorizzato).
Se i valori letterali cambiano (in base alle azioni dell'utente, ad esempio, come filtrare o ordinare i dati visualizzati), le query non trarranno vantaggio dalla memorizzazione nella cache (tranne occasionalmente quando corrispondono esattamente a una query recente).
Il modo per trarre vantaggio dalla memorizzazione nella cache con query "ad hoc" consiste nel parametrizzarle. Creazione di una query al volo in C# in questo modo:
int itemCount = 5;
string query = "DELETE FROM tblSTUFF WHERE ITEM_COUNT > " +
itemCount.ToString();
non è corretto. Il modo corretto (usando ADO.Net) sarebbe qualcosa del genere:
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();
}
La query non contiene valori letterali ed è già completamente parametrizzata, quindi le query successive che utilizzano l'istruzione parametrizzata identica utilizzerebbero il piano memorizzato nella cache (anche se chiamato con valori di parametro diversi). Nota che il codice qui è praticamente lo stesso del codice che useresti comunque per chiamare una stored procedure (l'unica differenza è CommandType e CommandText), quindi in qualche modo si riduce a dove vuoi che il testo di quella query "viva " (nel codice dell'applicazione o in una procedura memorizzata).
Infine, se per query "ad-hoc" intendi che stai costruendo dinamicamente query con colonne, tabelle, parametri di filtro diversi e quant'altro, come forse questi:
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
... allora praticamente non puoi fallo con le stored procedure (senza il file EXEC
hack di cui non si può parlare nella società educata), quindi il punto è controverso.
Aggiornamento 3: Ecco l'unico relativo alle prestazioni davvero buono motivo (che mi viene in mente, comunque) per l'utilizzo di una procedura memorizzata. Se la query è di lunga durata in cui il processo di compilazione del piano di esecuzione richiede molto più tempo dell'esecuzione effettiva e la query viene chiamata solo di rado (come un report mensile, ad esempio), è possibile inserirla in una stored procedure fare in modo che SQL Server mantenga il piano compilato nella cache abbastanza a lungo da poter essere ancora intorno al mese prossimo. Mi batte se è vero o no, però.