How to cancel a long-running Database operation?

2019-01-07 11:53发布

Currently working with Oracle, but will also need a solution for MS SQL.

I have a GUI that allows users to generate SQL that will be executed on the database. This can take a very long time, depending on the search they generate. I want the GUI/App to responsive during this search and I want the user to be able to cancel the search.

I'm using a Background Worker Thread.

My problem is that, when the user cancels the search, I can't interrupt the call to the database. It waits until it is finished and then, it can poll the 'CancelationPending' property. Not only does this waste resources on the database, but it creates problems for my code.

If the user hits 'Search' on a very long query, then clicks 'Cancel' and then 'Search' again - the first search is still chugging away on the database. The background worker is still busy when they hit search again. The only solution I've got to this problem is to make a new background worker.

It seems like a really ugly way to do things. The database keeps working I'm creating new instances of background workers....when I really want to STOP the database call and re-use the same worker.

How can I do that?

10条回答
ゆ 、 Hurt°
2楼-- · 2019-01-07 11:58

I dont think it is possible. Here is a link to a discussion on Oracle's website about this topic: http://forums.oracle.com/forums/thread.jspa?threadID=400492&start=15&tstart=0

查看更多
smile是对你的礼貌
3楼-- · 2019-01-07 11:58

If you're using an SQLCommand, you could try calling it's Cancel method.

查看更多
姐就是有狂的资本
4楼-- · 2019-01-07 11:58

KILL SESSION was the only working way for me to cancel the long running query. I am using the managed oracle provided and OracleCommand.Cancel() works some times but usually it's not working. Also OracleCommand.CommandTimeout is not respected according to my tests. Some time ago when i was using the unmanaged oracle provided i managed to cancel commands but not any more with the managed one. Any way killing the session was the only option. The query is not running on the UI thread but on a seperate thread. The cancel command is send from the UI thread. Its a little more complex because the application uses a middletier using WCF but at the end of the day i am killing the session. Of cource when running the query i have to find and save the session in order to kill it if necessary. There are many ways to find sid and serial# in order to kill the oracle session and i want waste your time explaining something you already know.

查看更多
倾城 Initia
5楼-- · 2019-01-07 12:06

What about opening a new connection to the database, login in as sysdba and sending a "ALTER SYSTEM KILL SESSION 'sid,serial#' IMMEDIATE" command specifying the SID of the process you want to terminate.

To get the sessionID: select sid from v$mystat where rownum = 1

To get Serial#: select sid, serial# from v$session where sid = :SID

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

EDIT: WW idea for not Login as sysdba here: http://forums.oracle.com/forums/thread.jspa?threadID=620578

查看更多
何必那么认真
6楼-- · 2019-01-07 12:09

I also noticed command.Cancel() doesn't really abort the command. What worked for me is closing the connection (rollback transaction if you use one) when the user aborts. This will raise an exception in your background thread while the command is executing, so you have to catch it and check the CancellationPending property there and not rethrow the exception in that case...

// 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)
{
    ...
}
查看更多
乱世女痞
7楼-- · 2019-01-07 12:12

I am pretty sure it is possible- we use TOAD for Oracle, and it lets you cancel long-running queries, as described here. I'm not sure how they do it though.

查看更多
登录 后发表回答