如何取消长时间运行的数据库操作?

时间:2009-05-20 17:00:36

标签: .net database oracle asynchronous backgroundworker

目前正在与Oracle合作,但还需要一个MS SQL解决方案。

我有一个GUI,允许用户生成将在数据库上执行的SQL。这可能需要很长时间,具体取决于它们生成的搜索。我希望GUI / App在此搜索期间能够响应,我希望用户能够取消搜索。

我正在使用后台工作线程。

我的问题是,当用户取消搜索时,我无法中断对数据库的调用。它等待它完成然后,它可以轮询'CancelationPending'属性。这不仅浪费了数据库上的资源,而且还为我的代码带来了问题。

如果用户在非常长的查询中点击“搜索”,然后点击“取消”,然后再次“搜索” - 第一次搜索仍然在数据库上消失。后台工作人员再次点击搜索时仍然很忙。我遇到这个问题的唯一方法就是建立一个新的后台工作者。

这似乎是一种非常丑陋的做事方式。数据库继续工作我正在创建后台工作者的新实例....当我真的想要停止数据库调用并重新使用同一个worker时。

我该怎么做?

11 个答案:

答案 0 :(得分:11)

如果您正在使用ADO.NET和SQL数据提供程序,请查看SqlCommand.Cancel方法。这就是你想要的。但是,它会尝试取消,取消可能需要一些时间。基本上,由SQL Server决定何时授予您的取消请求。取消查询时,您应该获得一个SqlException,指示操作已被用户取消。显然,您不希望将此异常视为异常并特别处理它,例如,如果SqlException是由于用户取消操作而导致的,只需将其吞下即可。

答案 1 :(得分:8)

我也注意到command.Cancel()并没有真正中止命令。对我来说有用的是当用户中止时关闭连接(如果你使用了回滚事务)。这将在命令执行时在后台线程中引发异常,因此你必须捕获它并检查那里的CancellationPending属性,而不是在那种情况下重新抛出异常......

// 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)
{
    ...
}

答案 2 :(得分:5)

我非常确定它可能 - 我们使用TOAD for Oracle,它允许您取消长时间运行的查询as described here。我不确定他们是怎么做的。

答案 3 :(得分:3)

我认为最佳解决方案似乎是通过监控表终止会话。

使用Oracle,您可以按照Burnsys的说法进行操作

在Firebird 2.5中,它会显示same

我希望Ms SQL

中存在类似的东西

答案 4 :(得分:3)

您可以让后台工作程序在不同的线程上触发实际的数据库调用,然后定期检查数据库调用是否已完成,或者是否已按下取消,此时您可以终止数据库线程。这实际上不会帮助数据库加载任何(因为您的查询已经发送并且仍在处理)但它确实会释放与之相关的本地资源。

答案 5 :(得分:2)

如果您使用的是SQLCommand,可以尝试调用它的Cancel方法。

答案 6 :(得分:2)

如何打开与数据库的新连接,以sysdba身份登录并发送“ALTER SYSTEM KILL SESSION'sid,serial#'IMMEDIATE”命令,指定要终止的进程的SID。

获取sessionID:从v $ mystat中选择sid,其中rownum = 1

要获得Serial#:从v $ session选择sid,serial#,其中sid =:SID

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

编辑:WW想法不在这里以sysdba身份登录:http://forums.oracle.com/forums/thread.jspa?threadID=620578

答案 7 :(得分:1)

我已尝试使用ADO 2.8和SQLOLEDB或SQL Server本机客户端取消和关闭。使用“取消”时,记录集将停止提取数据,但在后台,服务器的读取将继续并从应用程序中消耗内存。在32位应用程序中,几分钟后您可能会收到“内存不足”消息。当我关闭记录集(或连接,有或没有取消之前),ADO 2.8等待,直到获取所有记录。

我不知道ADO.NET是否做得更好,但我认为在取消/关闭后监控内存和网络访问是个好主意,以确保ADO真正停止读取数据。

答案 8 :(得分:0)

KILL SESSION是我取消长时间运行的查询的唯一工作方法。我正在使用提供的托管oracle,OracleCommand.Cancel()有时可以工作,但通常不起作用。另外,根据我的测试,不尊重OracleCommand.CommandTimeout。前一段时间,当我使用提供的非托管oracle时,我设法取消了命令,但托管的命令不再起作用。杀死会话的任何方法都是唯一的选择。该查询不是在UI线程上运行,而是在单独的线程上运行。取消命令是从UI线程发送的。它稍微复杂一点,因为该应用程序使用使用WCF的中间层,但是到最后,我终止了该会话。当然,在运行查询时,我必须找到并保存会话以在必要时将其杀死。有很多方法可以找到sid和serial#来终止oracle会话,我想浪费时间解释一些您已经知道的事情。

答案 9 :(得分:0)

Oracle在18c中引入了ALTER SYSTEM CANCEL SQL。您需要在SQL中添加带有UID的注释,然后查找类似的内容

SELECT S.SID||','||S.SERIAL#
                    FROM GV$SESSION S, V$SQL Q
                    WHERE S.USERNAME IS NOT NULL
                    AND S.STATUS = 'ACTIVE'
                    AND S.SQL_ID IS NOT NULL
                    AND Q.SQL_ID = S.SQL_ID
                    and sql_text like '%{queryId}%'

然后从.NET ALTER SYSTEM CANCEL SQL 'SID, SERIAL'

运行另一项操作

答案 10 :(得分:-1)

我认为不可能。以下是有关此主题的Oracle网站讨论的链接: http://forums.oracle.com/forums/thread.jspa?threadID=400492&start=15&tstart=0