Me encontré con un código de desarrollador donde el método SqlCommand.Prepare () (ver MSDN) se usa ampliamente antes de la ejecución de consultas SQL. Y me pregunto cuál es el beneficio de esto.
Muestra:
command.Prepare();
command.ExecuteNonQuery();
//...
command.Parameters[0].Value = 20;
command.ExecuteNonQuery();
He jugado un poco y rastreado. La ejecución del comando después de llamar al Prepare()
método hace que SQL Server ejecute la siguiente instrucción:
declare @p1 int
set @p1=1
exec sp_prepexec @p1 output,N'@id int,@desc text',N'INSERT INTO dbo.testtable (id) VALUES (@id)',@id=20'
select @p1
Después de eso, cuando el parámetro obtiene su valor y SqlCommand.ExecuteNonQuery()
se llama, se ejecuta lo siguiente en Sql-Server:
exec sp_execute 1,@id=20
Para mí, esto parece que la declaración se compila en cuanto Prepare()
se ejecuta. Me pregunto cuál es el beneficio de esto. ¿Significa esto que se coloca en la caché del plan y se puede reutilizar tan pronto como se ejecute la consulta final con los valores de los parámetros deseados?
Descubrí (y lo documenté en otra pregunta ) que los comandos SqlCommands que se ejecutan con SqlParameters siempre están envueltos en sp_executesql
llamadas a procedimientos. Esto hace que SQL Server pueda almacenar y reutilizar los planes independientemente de los valores de los parámetros.
Con respecto a esto, me pregunto si el prepare()
método es inútil u obsoleto o si me falta algo aquí.
Respuestas:
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:
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 ensp_prepexec
lugar de hacerlosp_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), elExecuteNonQuery
oExecuteReader
que sigue se ejecuta completamente, y si es ExecuteReader, devuelve filas. Aún así, SQL Server Profiler muestra que la primeraSqlCommand.Execute______()
llamada después de laSqlCommand.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ícitasp_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:
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:
SqlCommand.Prepare()
parece ser que no necesita enviar el texto de la consulta nuevamente a través de la red,sp_prepare
ysp_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 ;-).fuente
sp_prepare
la llamada asp_executesql
, solo para estar seguro. Para el paso 10, sí, hice más pruebas ayer y vi algunas ocasiones con diferentes identificadores parasp_prepare
, pero aún así nunca fue lo mismosp_executesql
. Probaré una cosa más y actualizaré mi respuesta.sp_executesql
que no puede o no. Pero independientemente, el tiempo de análisis aún está allísp_prepare
si el plan se ha eliminado del caché, por lo que eliminaré esa parte de mi respuesta (interesante pero irrelevante). Esa parte fue más una nota al margen interesante de todos modos ya que no abordaba su pregunta directa. Todo lo demás todavía se aplica.Diría que el método Prepare tiene un mundo SqlCommand de valor limitado, no es que sea completamente inútil. Una ventaja es que Preparar es parte de la interfaz IDbCommand que implementa SqlCommand. Esto permite que el mismo código se ejecute contra otros proveedores de DBMS (que pueden requerir Preparar) sin lógica condicional para determinar si Preparar debe ser llamado o no.
Prepare no tiene ningún valor con .NET Provider para SQL Server además de estos casos de uso, AFAIK.
fuente