¿Cómo cancelar una operación de base de datos de larga duración?

Actualmente trabaja con Oracle, pero también necesitará una solución para MS SQL.

Tengo una GUI que permite a los usuarios generar SQL que se ejecutará en la base de datos. Esto puede llevar mucho tiempo, según la búsqueda que generen. Quiero que la GUI / aplicación responda durante esta búsqueda y quiero que el usuario pueda cancelar la búsqueda.

Estoy usando un hilo de trabajador de fondo.

Mi problema es que, cuando el usuario cancela la búsqueda, no puedo interrumpir la llamada a la base de datos. Espera hasta que se termine y luego, puede sondear la propiedad ‘CancelationPending’. No solo esto desperdicia recursos en la base de datos, sino que crea problemas para mi código.

Si el usuario pulsa ‘Buscar’ en una consulta muy larga, luego hace clic en ‘Cancelar’ y luego ‘Buscar’ otra vez – la primera búsqueda todavía está desapareciendo en la base de datos. El trabajador de fondo todavía está ocupado cuando vuelven a buscar. La única solución que tengo para este problema es crear un nuevo trabajador de segundo plano.

Parece una manera realmente fea de hacer cosas. La base de datos sigue funcionando Estoy creando nuevas instancias de trabajadores en segundo plano … cuando realmente quiero PARAR la llamada a la base de datos y volver a usar el mismo trabajador.

¿Cómo puedo hacer eso?

No creo que sea posible. Aquí hay un enlace a una discusión en el sitio web de Oracle sobre este tema: http://forums.oracle.com/forums/thread.jspa?threadID=400492&start=15&tstart=0

Si está utilizando ADO.NET y el proveedor de datos SQL, eche un vistazo al método SqlCommand.Cancel. Eso hace lo que estás buscando. Sin embargo, intenta cancelar y la cancelación puede llevar tiempo. Básicamente, depende de SQL Server decidir cuándo otorgar su solicitud de cancelación. Cuando se cancela la consulta, debe obtener una SqlException que indique que el usuario canceló la operación. Aparentemente, no desea tratar esta excepción como excepción y manejarla especialmente, como si SqlException se debe a que el usuario canceló la operación, simplemente tráguela.

También noté que command.Cancel () realmente no cancela el comando. Lo que funcionó para mí es cerrar la conexión (transacción de reversión si usa uno) cuando el usuario aborta. Esto generará una excepción en el hilo de fondo mientras el comando se está ejecutando, por lo que debe atraparlo y verificar allí la propiedad CancellationPending y no volver a lanzar la excepción en ese caso …

// When aborting worker.CancelAsync(); command.Connection.Close(); // In your DoWork event handler ... catch (Exception) { if (worker.CancellationPending) { e.Cancel = true; return; } else { throw; } } // And in your RunWorkerCompleted event handler if (e.Error == null && !e.Cancelled) { ... } 

Estoy bastante seguro de que es posible : utilizamos TOAD for Oracle y le permite cancelar consultas de larga ejecución, como se describe aquí . Aunque no estoy seguro de cómo lo hacen.

Puede hacer que el trabajador de fondo inicie la llamada real de la base de datos en un hilo diferente y luego verifique periódicamente si la llamada a la base de datos ha finalizado o si se ha cancelado, en ese punto podría eliminar el hilo de la base de datos. En realidad, esto no ayudaría a cargar la base de datos (ya que su consulta se ha enviado y aún se está procesando), pero libera los recursos locales relacionados con ella.

Creo que la mejor solución parece matar sesiones a través de la tabla de monitoreo.

Con Oracle puedes hacerlo como dice Burnsys

En Firebird 2.5 se verá igual

Espero que exista algo similar en MS SQL

Si está utilizando un SQLCommand, puede intentar llamarlo método de cancelación .

¿Qué tal abrir una nueva conexión a la base de datos, iniciar sesión como sysdba y enviar un comando sid, serial serial ‘INMEDIATE’ “ALTER SYSTEM KILL SESSION” especificando el SID del proceso que desea finalizar.

Para obtener el ID de sesión: seleccione sid de v $ mystat donde rownum = 1

Para obtener el número de serie: seleccione sid, número de serie de v $ session donde sid =: SID

http://www.oracle-base.com/articles/misc/KillingOracleSessions.php

EDITAR: idea de WW para no iniciar sesión como sysdba aquí: http://forums.oracle.com/forums/thread.jspa?threadID=620578

He intentado cancelar y cerrar con ADO 2.8 y SQLOLEDB o cliente nativo de SQL Server. Con Cancel, el conjunto de registros deja de recuperar datos, pero en el fondo la lectura del servidor continúa y consume memoria de la aplicación. En una aplicación de 32 bits, puede ocurrir que reciba un mensaje de “falta de memoria” unos minutos más tarde. Cuando cierro el conjunto de registros (o la conexión, con o sin cancelar antes), ADO 2.8 espera hasta que se recuperen todos los registros.

No sé si ADO.NET lo hace mejor, pero creo que es una buena idea supervisar la memoria y el acceso a la red después de Cancelar / Cerrar para asegurarse de que ADO realmente deje de leer datos.