Preparing a SQL batch separately from executing the prepared SQL batch is a construct that is effectively** useless for SQL Server given how execution plans are cached. Separating out the steps of preparation (parsing, binding any parameters, and compiling) and execution only makes sense when there is no caching. The purpose is to save the time spent on parsing and compiling by reusing an existing plan, and this is what the internal plan cache does. But not all RDBMS's do this level of caching (clearly from the existence of these functions) and so it is left up to the client code to request that a plan be "cached", and so save that cache ID, and then re-use it. This is additional burden on the client code (and programmer to remember to do it and get it right) that is unnecessary. In fact, the MSDN page for the IDbCommand.Prepare() method states:
The server automatically caches plans for reuse as necessary; therefore, there is no need to call this method directly in your client application.
It should be noted that, as far as my testing shows (which matches what you show in the Question), calling SqlCommand.Prepare(), does not do a "prepare"-only operation: it calls sp_prepexec which prepares and executes the SQL; it does not call sp_prepare which is parse and compile only (and does not take in any parameter values, only their names and datatypes). Hence, there cannot be any "pre-compile" benefit of calling
SqlCommand.Prepare since it does an immediate execution (assuming the goal is to defer execution). HOWEVER, there's an interesting potential minor benefit of calling
SqlCommand.Prepare(), even if it does call
sp_prepexec instead of
sp_prepare. Please see the note (**) at the bottom for details.
My testing shows that even when
SqlCommand.Prepare() is called (and please note that this call does not issue any commands on SQL Server), the
ExecuteReader that follows is fully executed, and if it is ExecuteReader, it returns rows. Still, SQL Server Profiler does show that the first
SqlCommand.Execute______() call after the
SqlCommand.Prepare() call registers as a "Prepare SQL" event, and subsequent
.Execute___() calls register as "Exec Prepared SQL" events.
It has been uploaded to PasteBin at: http://pastebin.com/Yc2Tfvup.
The code creates a .NET / C# Console App that is intended to run while a SQL Server Profiler trace is running (or an Extended Events session). It pauses after each step so it will be clear which statements have a particular effect.
Found more info, and a slight potential reason to avoid calling
SqlCommand.Prepare(). One thing I noticed in my testing, and what should be noted for anyone running that test Console App: there is never an explicit call made
sp_unprepare. I did some digging and found the following post in the MSDN forums:
SqlCommand - Prepare or not to prepare?
The accepted answer contains a bunch of info, but the salient points are:
- ** What prepare actually saves you is primarily the time it takes to transmit the query string over the wire.
- A prepared query has no guarantee of a cached plan being saved, and is no faster than an ad-hoc query once it reaches the server.
- RPC's (CommandType.StoredProcedure) gain nothing from preparing.
- On the server, a prepared handle is basically an index into an in-memory map containing TSQL to execute.
- The map is stored on a per-connection basis, no cross-connection use is possible.
- The reset sent when the client re-uses the connection from the pool clears the map.
I also found the following post from the SQLCAT team, which appears to be related, but could simply be an issue specific to ODBC whereas SqlClient does clean up correctly upon disposing of the SqlConnection. It is hard to say since the SQLCAT post does not mention any additional tests that would help prove this as a cause, such as clearing the connection pool, etc.
Watch out those prepared SQL statements
- the only real benefit of calling
SqlCommand.Prepare() seems to be that you don't need to submit the query text again over the network,
sp_prepexec store the query text as part of the connection's memory (i.e. SQL Server memory)
I would recommend against calling
SqlCommand.Prepare() because the only potential benefit is saving network packets while the downside is taking up more server memory. While the amount of memory consumed is probably very small in the majority of cases, network bandwidth is rarely ever an issue since most DB servers are directly connected to the app servers over 10 Megabit minimum (more likely 100 Megabit or Gigabit these days) connections (and some are even on the same box ;-). That and memory is nearly always a more scarce resource than network bandwidth.
I suppose, for anyone writing software that can connect to multiple databases, then the convenience of a standard interface might change this cost/benefit analysis. But even in that case, I have to believe that it is still easy enough to abstract a "standard" DB interface that allows for different providers (and hence differences between them, such as not needing to call
Prepare() ) given that doing so is basic Object Oriented programming that you are likely already doing in your app code ;-).