Preparar un lote SQL por separado de ejecutar el lote SQL preparado es una construcción que es efectiva ** 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 ejecución solo tiene sentido cuando no hay almacenamiento en caché. El propósito es ahorrar el tiempo dedicado a analizar y compilar reutilizando un plan existente, y esto es lo que hace el caché interno del plan. Pero no todos los RDBMS realizan este nivel de almacenamiento en caché (claramente por la existencia de estas funciones) y, por lo tanto, le corresponde al código del cliente solicitar que un plan se "almacene en caché", y así guardar esa ID de caché, y luego reutilizarla eso. Esta es una carga adicional sobre el código del cliente (y el programador para recordar hacerlo y hacerlo bien) que es innecesario. De hecho, la página de MSDN paraIDbCommand.Prepare () método establece:
El servidor almacena automáticamente en caché los planes de reutilización 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, en lo que mi muestra de prueba (que coincide con lo que mostramos en la pregunta), llamando SqlCommand.Prepare () , no no hacer un "preparar" la operación -sólo: llama 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 de "precompilación" de la llamada, SqlCommand.Prepare
ya que realiza una ejecución inmediata (suponiendo que el objetivo es diferir la ejecución). SIN EMBARGO , hay un interesante beneficio menor potencial de llamarSqlCommand.Prepare()
, incluso si llama en sp_prepexec
lugar de hacerlo sp_prepare
. Por favor vea la nota (** ) en la parte inferior para más detalles.
Mi prueba muestra que incluso cuando SqlCommand.Prepare()
se llama (y tenga en cuenta que esta llamada no emite ningún 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______()
llamada después de la SqlCommand.Prepare()
llamada se registra como un evento "Preparar SQL", y las .Execute___()
llamadas posteriores 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 del Analizador de SQL Server (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 un montón de información, pero los puntos más destacados son:
- ** ** Lo que prepara realmente le ahorra es principalmente el tiempo que lleva transmitir la cadena de consulta a través del cable.
- Una consulta preparada no tiene garantía de que se guarde un plan en caché, y no es más rápido que una consulta ad-hoc una vez que llega al servidor.
- Los RPC (CommandType.StoredProcedure) no obtienen nada de 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 restablecimiento enviado cuando el cliente reutiliza la conexión del grupo borra el mapa.
También encontré la siguiente publicación del equipo de 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 pueda ayudar a probar esto como una causa, como borrar el grupo de conexiones, etc.
Cuidado con las declaraciones SQL preparadas
CONCLUSIÓN
Dado que:
- El único beneficio real de las llamadas
SqlCommand.Prepare()
parece ser que no necesita enviar el texto de la consulta nuevamente a través de la red,
- llamar
sp_prepare
y sp_prepexec
almacenar el texto de la consulta como parte de la memoria de la conexión (es decir, la memoria del servidor SQL)
Recomendaría no llamar SqlCommand.Prepare()
porque el único beneficio potencial es guardar 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 bases de datos están conectados directamente a los servidores de aplicaciones con conexiones de más de 10 megabits (probablemente 100 megabits o gigabits en estos días) ( y algunos incluso están en la misma caja ;-). Eso y la memoria es 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 múltiples 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 tener que llamar Prepare()
) dado que hacerlo es un objeto básico Programación orientada que probablemente ya esté haciendo en el código de su aplicación ;-).