Solución:
La preparación de un lote de SQL por separado de la ejecución del lote de SQL preparado es una construcción que efectivamente** inútil para SQL Server dada la forma en que se almacenan en caché los planes de ejecución. Separar los pasos de preparación (análisis, vinculación de parámetros y compilación) y la ejecución solo tiene sentido cuando no hay almacenamiento en caché. El propósito es ahorrar el tiempo invertido en analizar y compilar reutilizando un plan existente, y esto es lo que hace el caché de plan interno. Pero no todos los RDBMS hacen este nivel de almacenamiento en caché (claramente a partir de la existencia de estas funciones), por lo que se deja en manos del código del cliente solicitar que un plan se “almacene en caché” y, por lo tanto, guarde ese ID de caché y luego vuelva a utilizarlo. eso. Esta es una carga adicional para el código del cliente (y el programador debe recordar hacerlo y hacerlo bien) que es innecesario. De hecho, la página de MSDN para el método IDbCommand.Prepare () dice:
El servidor almacena en caché automáticamente los planes para reutilizarlos según sea necesario; por lo tanto, no es necesario llamar a este método directamente en su aplicación cliente.
Cabe señalar que, por lo que mi prueba muestra (que coincide con lo que muestra en la Pregunta), llamar a SqlCommand.Prepare (), no no hacer una operación “prepare” -only: llama a sp_prepexec que prepara y ejecuta el SQL; no llama a sp_prepare, que solo analiza y compila (y no toma ningún valor de parámetro, solo sus nombres y tipos de datos). Por lo tanto, no puede haber ningún beneficio “precompilado” de llamar SqlCommand.Prepare
ya que realiza una ejecución inmediata (asumiendo que el objetivo es diferir la ejecución). SIN EMBARGO, hay un beneficio menor potencial interesante de llamar SqlCommand.Prepare()
, incluso si llama sp_prepexec
en lugar de sp_prepare
. Por favor vea la nota (**) en la parte inferior para obtener más detalles.
Mis pruebas muestran que incluso cuando SqlCommand.Prepare()
se llama (y tenga en cuenta que esta llamada no no emitir cualquier comando en SQL Server), el ExecuteNonQuery
o ExecuteReader
que sigue se ejecuta completamente, y si es ExecuteReader, devuelve filas. Aún así, SQL Server Profiler muestra que la primera SqlCommand.Execute______()
llamar después de la SqlCommand.Prepare()
registros de llamadas como un evento “Preparar SQL” y, posteriormente, .Execute___()
las llamadas se registran como eventos “Exec Prepared SQL”.
Código de prueba
Se ha subido a PasteBin en: http://pastebin.com/Yc2Tfvup. El código crea una aplicación de consola .NET / C # que está diseñada para ejecutarse mientras se ejecuta un seguimiento de SQL Server Profiler (o una sesión de eventos extendidos). Hace una pausa después de cada paso para que quede claro qué declaraciones tienen un efecto particular.
ACTUALIZAR
Encontré más información y una pequeña razón potencial para evitar llamar SqlCommand.Prepare()
. Una cosa que noté en mis pruebas, y lo que debería tenerse en cuenta para cualquiera que ejecute esa aplicación de consola de prueba: nunca se realiza una llamada explícita sp_unprepare
. Investigué un poco y encontré la siguiente publicación en los foros de MSDN:
SqlCommand – ¿Prepararse o no prepararse?
La respuesta aceptada contiene mucha información, pero los puntos destacados son:
- ** Lo que realmente le ahorra preparar es principalmente el tiempo que lleva transmitir la cadena de consulta a través del cable.
- Una consulta preparada no garantiza que se guarde un plan almacenado en caché y no es más rápida que una consulta ad-hoc una vez que llega al servidor.
- Los RPC (CommandType.StoredProcedure) no ganan nada con la preparación.
- En el servidor, un identificador preparado es básicamente un índice en un mapa en memoria que contiene TSQL para ejecutar.
- El mapa se almacena por conexión, no es posible el uso de conexión cruzada.
- El reinicio enviado cuando el cliente reutiliza la conexión del grupo borra el mapa.
También encontré la siguiente publicación del equipo SQLCAT, que parece estar relacionada, pero podría ser simplemente un problema específico de ODBC, mientras que SqlClient se limpia correctamente al deshacerse de SqlConnection. Es difícil de decir ya que la publicación de SQLCAT no menciona ninguna prueba adicional que ayude a probar esto como una causa, como borrar el grupo de conexiones, etc.
Cuidado con esas declaraciones SQL preparadas
CONCLUSIÓN
Dado que:
- el único beneficio real de llamar
SqlCommand.Prepare()
parece ser que no es necesario volver a enviar el texto de la consulta a través de la red, - vocación
sp_prepare
ysp_prepexec
almacenar el texto de la consulta como parte de la memoria de la conexión (es decir, la memoria de SQL Server)
yo recomendaria contra vocación SqlCommand.Prepare()
porque el único beneficio potencial es ahorrar paquetes de red mientras que la desventaja es ocupar más memoria del servidor. Si bien la cantidad de memoria consumida es probablemente muy pequeña en la mayoría de los casos, el ancho de banda de la red rara vez es un problema, ya que la mayoría de los servidores de base de datos están conectados directamente a los servidores de aplicaciones con conexiones mínimas de 10 Megabit (más probablemente 100 Megabit o Gigabit en estos días) ( y algunos incluso están en la misma caja ;-). Eso y la memoria son casi siempre un recurso más escaso que el ancho de banda de la red.
Supongo que para cualquiera que escriba software que pueda conectarse a varias bases de datos, la conveniencia de una interfaz estándar podría cambiar este análisis de costo / beneficio. Pero incluso en ese caso, tengo que creer que todavía es bastante fácil abstraer una interfaz de base de datos “estándar” que permite diferentes proveedores (y, por lo tanto, diferencias entre ellos, como no necesitando llamar Prepare()
) dado que hacerlo es una programación básica orientada a objetos que probablemente ya esté haciendo en el código de su aplicación ;-).
Con respecto a esto, me pregunto si el método prepare () es algo inútil u obsoleto o si me falta algo aquí.
Yo diría que el método Prepare tiene un valor limitado en el mundo SqlCommand, no es que sea completamente inútil. Un beneficio es que Prepare es parte de la interfaz IDbCommand que implementa SqlCommand. Esto permite que el mismo código se ejecute en otros proveedores de DBMS (que pueden requerir Preparar) sin lógica condicional para determinar si se debe llamar a Preparar o no.
Prepare no tiene ningún valor con el proveedor .NET para SQL Server además de estos casos de uso, AFAIK.