28

目前正在与 Oracle 合作,但也需要 MS SQL 的解决方案。

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

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

我的问题是,当用户取消搜索时,我无法中断对数据库的调用。它一直等到完成,然后它可以轮询“CancelationPending”属性。这不仅会浪费数据库上的资源,还会给我的代码带来问题。

如果用户在很长的查询上点击“搜索”,然后点击“取消”,然后再次点击“搜索”——第一次搜索仍在数据库中进行。当他们再次点击搜索时,后台工作人员仍然很忙。我对这个问题的唯一解决方案是创建一个新的后台工作人员。

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

我怎样才能做到这一点?

4

11 回答 11

10

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

于 2009-05-20T17:36:18.190 回答
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)
{
    ...
}
于 2010-09-13T10:02:34.883 回答
5

我很确定这是可能的——我们使用 TOAD for Oracle,它可以让您取消长时间运行的查询,如此处所述。我不确定他们是怎么做到的。

于 2009-05-21T17:02:53.820 回答
3

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

于 2009-05-20T17:09:01.770 回答
3

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

使用 Oracle,您可以像 Burnsys 所说的那样做到这一点

在 Firebird 2.5 中它看起来是一样的

我希望 SQL 女士中存在类似的东西

于 2009-05-20T19:45:04.077 回答
2

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

于 2009-05-20T17:17:47.790 回答
2

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

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

要获取序列号:从 v$session 中选择 sid、序列号,其中 sid = :SID

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

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

于 2009-05-20T17:19:07.350 回答
1

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

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

于 2012-05-23T09:16:03.473 回答
1

KILL SESSION 是我取消长时间运行的查询的唯一工作方式。我正在使用提供的托管 oracle,并且 OracleCommand.Cancel() 有时可以工作,但通常无法正常工作。根据我的测试,OracleCommand.CommandTimeout 也不受尊重。前段时间,当我使用非托管 oracle 时,我设法取消了命令,但不再使用托管的。以任何方式终止会话是唯一的选择。查询不在 UI 线程上运行,而是在单独的线程上运行。取消命令是从 UI 线程发送的。它有点复杂,因为应用程序使用了一个使用 WCF 的中间层,但最终我要终止会话。当然,在运行查询时,我必须找到并保存会话,以便在必要时将其终止。

于 2018-09-11T16:06:42.390 回答
0

OracleALTER SYSTEM CANCEL SQL在 18c 中引入。您需要在 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'

于 2019-07-27T06:42:00.253 回答
-2

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

于 2009-05-20T17:06:45.893 回答