Preparare un batch SQL separatamente dall'esecuzione del batch SQL preparato è un costrutto efficacemente **inutile per SQL Server dato come vengono memorizzati nella cache i piani di esecuzione. Separare le fasi di preparazione (analisi, associazione di qualsiasi parametro e compilazione) ed esecuzione ha senso solo in assenza di memorizzazione nella cache. Lo scopo è quello di risparmiare tempo dedicato all'analisi e alla compilazione riutilizzando un piano esistente, ed è ciò che fa la cache del piano interno. Ma non tutti gli RDBMS eseguono questo livello di memorizzazione nella cache (chiaramente dall'esistenza di queste funzioni) e quindi è lasciato al codice client richiedere che un piano sia "memorizzato nella cache", quindi salvare l'ID cache e riutilizzarlo esso. Questo è un onere aggiuntivo per il codice client (e per il programmatore ricordarsi di farlo e farlo bene) che non è necessario. In effetti, la pagina MSDN per il metodo IDbCommand.Prepare () afferma:
Il server memorizza automaticamente nella cache i piani per il riutilizzo, se necessario; pertanto, non è necessario chiamare questo metodo direttamente nell'applicazione client.
Va notato che, per quanto riguarda i miei test (che corrispondono a ciò che mostri nella Domanda), chiamando SqlCommand.Prepare () , non esegue un'operazione "solo": chiama sp_prepexec che prepara ed esegue l'SQL ; non chiama sp_prepare che è solo analisi e compilazione (e non accetta alcun valore di parametro, ma solo i loro nomi e tipi di dati). Quindi, non ci può essere alcun vantaggio di "pre-compilazione" della chiamata SqlCommand.Prepare
poiché esegue un'esecuzione immediata (supponendo che l'obiettivo sia ritardare l'esecuzione). TUTTAVIA , c'è un interessante potenziale vantaggio minore di chiamare SqlCommand.Prepare()
, anche se chiama sp_prepexec
invece di sp_prepare
. Si prega di consultare la nota (** ) in fondo per i dettagli.
I miei test mostrano che anche quando SqlCommand.Prepare()
viene chiamato (e si noti che questa chiamata non emette alcun comando su SQL Server), il ExecuteNonQuery
o ExecuteReader
che segue viene eseguito completamente e, se è ExecuteReader, restituisce righe. Tuttavia, SQL Server Profiler mostra che la prima SqlCommand.Execute______()
chiamata dopo la SqlCommand.Prepare()
chiamata viene registrata come evento "Prepara SQL" e le .Execute___()
chiamate successive vengono registrate come eventi "Exec Prepared SQL".
Codice test
È stato caricato su PasteBin su: http://pastebin.com/Yc2Tfvup . Il codice crea un'app console .NET / C # che deve essere eseguita mentre è in esecuzione una traccia di SQL Server Profiler (o una sessione di eventi estesi). Si interrompe dopo ogni passaggio, quindi sarà chiaro quali istruzioni hanno un effetto particolare.
AGGIORNARE
Abbiamo trovato maggiori informazioni e un leggero potenziale motivo per evitare di chiamare SqlCommand.Prepare()
. Una cosa che ho notato nei miei test e che cosa dovrebbe essere notato per chiunque esegua quell'app Console di test: non viene mai fatta una chiamata esplicita sp_unprepare
. Ho fatto qualche ricerca e ho trovato il seguente post nei forum MSDN:
SqlCommand - Preparare o non preparare?
La risposta accettata contiene un sacco di informazioni, ma i punti salienti sono:
- ** Ciò che ti prepara effettivamente ti salva è principalmente il tempo necessario per trasmettere la stringa di query sul filo.
- Una query preparata non garantisce il salvataggio di un piano memorizzato nella cache e non è più veloce di una query ad hoc una volta raggiunta il server.
- Gli RPC (CommandType.StoredProcedure) non ottengono nulla dalla preparazione.
- Sul server, un handle preparato è fondamentalmente un indice in una mappa in memoria contenente TSQL da eseguire.
- La mappa viene memorizzata in base alla connessione, non è possibile utilizzare la connessione incrociata.
- Il reset inviato quando il client riutilizza la connessione dal pool cancella la mappa.
Ho anche trovato il seguente post del team SQLCAT, che sembra essere correlato, ma potrebbe semplicemente essere un problema specifico di ODBC mentre SqlClient si ripulisce correttamente dopo aver eliminato SqlConnection. È difficile da dire dal momento che il post SQLCAT non menziona alcun test aggiuntivo che possa aiutare a dimostrare questo come causa, come cancellare il pool di connessioni, ecc.
Fai attenzione a quelle istruzioni SQL preparate
CONCLUSIONE
Dato che:
- l'unico vero vantaggio della chiamata
SqlCommand.Prepare()
sembra essere che non è necessario inviare nuovamente il testo della query sulla rete,
- chiamare
sp_prepare
e sp_prepexec
archiviare il testo della query come parte della memoria della connessione (ad es. memoria di SQL Server)
Vorrei raccomandare contro chiamando SqlCommand.Prepare()
perché l'unico potenziale beneficio è salvare i pacchetti di rete, mentre il rovescio della medaglia sta prendendo più memoria del server. Sebbene la quantità di memoria consumata sia probabilmente molto ridotta nella maggior parte dei casi, la larghezza di banda della rete raramente è mai un problema poiché la maggior parte dei server DB è direttamente connessa ai server delle app con un minimo di 10 Megabit (più probabilmente 100 Megabit o Gigabit in questi giorni) ( e alcuni sono persino nella stessa scatola ;-). Quello e la memoria sono quasi sempre una risorsa più scarsa della larghezza di banda della rete.
Suppongo che, per chiunque scriva software in grado di connettersi a più database, la praticità di un'interfaccia standard potrebbe cambiare questa analisi costi / benefici. Ma anche in quel caso, devo credere che sia ancora abbastanza facile astrarre un'interfaccia DB "standard" che consenta diversi provider (e quindi differenze tra loro, come non dover chiamare Prepare()
) dato che farlo è Basic Object Programmazione orientata che probabilmente stai già facendo nel codice dell'app ;-).