Necesito devolver un resultado parcial (como selección simple) de un procedimiento almacenado antes de que finalice.
¿Es posible hacer eso?
Si es así, ¿cómo hacer eso?
Si no, ¿alguna solución?
EDITAR: Tengo varias partes del procedimiento. En la primera parte calculo varias cuerdas. Los uso más adelante en el procedimiento para realizar operaciones adicionales. El problema es que la persona que llama necesita la cadena lo antes posible. Por lo tanto, necesito calcular esa cadena y volver a pasarla (de alguna manera, desde una selección, por ejemplo) y luego continuar trabajando. La persona que llama obtiene su valiosa cadena mucho más rápido.
La persona que llama es un servicio web.
sql-server-2012
t-sql
stored-procedures
multi-thread
web-service
Bogdan Bogdanov
fuente
fuente
Respuestas:
Probablemente esté buscando el
RAISERROR
comando con laNOWAIT
opción.Por las observaciones :
Esto no devuelve los resultados de una
SELECT
declaración, pero le permitirá pasar mensajes / cadenas al cliente. Si desea devolver un subconjunto rápido de los datos que está seleccionando, puede considerar laFAST
sugerencia de la consulta.Agregado por Shannon Severance en un comentario:
Del error y manejo de transacciones en SQL Server por Erland Sommarskog:
Vea el artículo fuente para el contexto completo.
fuente
FAST
resolvió el problema para mí en un problema en el que necesitaba sincronizar la ejecución de un procedimiento almacenado y un código C # para exacerbar y reproducir una condición de carrera. Es más fácil consumir conjuntos de resultados mediante programación que usar algo como estoRAISERROR()
. Cuando comencé a leer tu respuesta, parecía que estabas diciendo que no se puede hacerSELECT
así que, ¿tal vez eso podría aclararse?ACTUALIZACIÓN: Vea la respuesta de strutzky ( arriba ) y los comentarios para al menos un ejemplo donde esto no se comporta como esperaba y describo aquí. Tendré que experimentar / leer más para actualizar mi comprensión cuando el tiempo lo permita ...
Si su interlocutor interactúa con la base de datos de forma asincrónica o está enhebrado / multiproceso, por lo que puede abrir una segunda sesión mientras la primera aún se está ejecutando, puede crear una tabla para contener los datos parciales y actualizarla a medida que avanza el procedimiento. Esto se puede leer en una segunda sesión con el nivel de aislamiento de transacción 1 configurado para permitirle leer los cambios no confirmados:
1: según los comentarios y la actualización posterior en la respuesta de srutzky, no se requiere establecer el nivel de aislamiento si el proceso que se está monitoreando no está envuelto en una transacción, aunque tiendo a dejarlo fuera de la costumbre en tales circunstancias, ya que no causa daño cuando no es necesario en estos casos
Por supuesto, si pudiera tener múltiples procesos operando de esta manera (lo que es probable si su servidor web acepta usuarios concurrentes y es muy raro que ese no sea el caso) deberá identificar la información de progreso de este proceso de alguna manera . Quizás pase el procedimiento con un UUID recién acuñado como clave, agréguelo a la tabla de progreso y lea con:
He usado este método para monitorear procesos manuales de larga ejecución en SSMS. Sin embargo, no puedo decidir si "huele" demasiado para que considere usarlo en la producción ...
fuente
El OP ya ha intentado enviar múltiples conjuntos de resultados (no MARS) y ha visto que realmente espera a que se complete el Procedimiento almacenado antes de devolver cualquier conjunto de resultados. Con esa situación en mente, aquí hay algunas opciones:
Si sus datos son lo suficientemente pequeños como para caber dentro de 128 bytes, lo más probable es
SET CONTEXT_INFO
que pueda usar lo que debería hacer que ese valor sea visible a través deSELECT [context_info] FROM [sys].[dm_exec_requests] WHERE [session_id] = @SessionID;
. Sólo se necesita para ejecutar una consulta rápida antes de ejecutar el procedimiento almacenado aSELECT @@SPID;
y agarrar esa víaSqlCommand.ExecuteScalar
.Acabo de probar esto y funciona.
Similar a la sugerencia de @ David de poner los datos en una tabla de "progreso", pero sin necesidad de meterse con problemas de limpieza o concurrencia / separación de procesos:
Guid
dentro del código de la aplicación y páselo como parámetro al Procedimiento almacenado. Almacene este Guid en una variable, ya que se usará varias veces.CREATE TABLE ##MyProcess_{GuidFromApp};
. La tabla puede tener las columnas de cualquier tipo de datos que necesite.Siempre que tenga los datos, insértelos en esa tabla de temperatura global.
En el código de la aplicación, iniciar intentar leer los datos, pero envolver el
SELECT
de unaIF EXISTS
manera que no se producirá un error si la tabla no se ha creado todavía:Con
String.Format()
, puede reemplazar{0}
con el valor en la variable Guid. Verifique siReader.HasRows
es verdadero, luego lea los resultados, de lo contrario llameThread.Sleep()
o lo que sea para luego sondear nuevamente.Beneficios:
EXEC
/sp_executesql
)He probado esto y funciona como se esperaba. Puede probarlo usted mismo con el siguiente código de ejemplo.
En una pestaña de consulta, ejecute lo siguiente y luego resalte las 3 líneas en el comentario de bloque y ejecútelo:
Vaya a la pestaña "Mensajes" y copie el GUID que se imprimió. Luego, abra otra pestaña de consulta y ejecute lo siguiente, colocando el GUID que copió de la pestaña Mensajes de la otra sesión en la inicialización de variables en la línea 1:
Sigue golpeando F5. Debería ver 1 entrada durante los primeros 10 segundos y luego 2 entradas durante los próximos 10 segundos.
Puede usar SQLCLR para devolver una llamada a su aplicación a través de un servicio web o por algún otro medio.
Tal vez podría usar
PRINT
/RAISERROR(..., 1, 10) WITH NOWAIT
para pasar las cadenas de inmediato, pero esto sería un poco complicado debido a los siguientes problemas:VARCHAR(8000)
oNVARCHAR(4000)
Los mensajes, por defecto, tampoco se envían hasta que se completa el proceso. Sin embargo, este comportamiento puede modificarse estableciendo la propiedad SqlConnection.FireInfoMessageEventOnUserErrors en
true
. La documentación dice:La desventaja aquí es que la mayoría de los errores de SQL ya no generarán a
SqlException
. En este caso, debe probar propiedades de eventos adicionales que se pasan al controlador de eventos de mensajes. Esto es válido para toda la conexión, lo que hace las cosas un poco más complicadas, pero no inmanejables.Todos los mensajes aparecen en el mismo nivel sin campo o propiedad separados para distinguir uno del otro. El orden en que se reciben debe ser el mismo que el de su envío, pero no estoy seguro de si eso es lo suficientemente confiable. Es posible que deba incluir una etiqueta o algo que luego pueda analizar. De esa manera, al menos podría estar seguro de cuál es cuál.
fuente
RETURN
declaración). Entonces no está funcionando.SqlConnection
? ¿Cuántos datos quieres devolver? ¿Qué tipos de datos? ¿Has probado algunaPRINT
oRAISERROR WITH NOWAIT
?Si su procedimiento almacenado necesita ejecutarse en segundo plano (es decir, de forma asíncrona), entonces debe usar Service Broker. Es un poco complicado configurarlo, pero una vez hecho esto, podrá iniciar el procedimiento almacenado (sin bloqueo) y escuchar los mensajes de progreso durante el tiempo que desee.
fuente