C# · 12月 27, 2021

c# – 如何对可以取消的DataTable操作执行SQL查询

我试图使标题尽可能具体.基本上我现在在后台工作线程中运行的是一些如下代码: @H_419_2@sqlConnection conn = new sqlConnection(connstring); sqlCommand cmd = new sqlCommand(query,conn); conn.open(); sqlDataAdapter sda = new sqlDataAdapter(cmd); sda.Fill(Results); conn.Close(); sda.Dispose();

where是一个表示大而耗时的查询的字符串,conn是连接对象.

我现在的问题是我需要一个停止按钮.我已经意识到杀死后台工作者是没有价值的,因为我仍然希望在取消查询后保留剩下的结果.此外,直到查询后,才能检查取消的状态.

到目前为止我已经想到了

我一直在努力概念化如何有效地处理这个问题,而不会造成太大的性能损失.

我的想法是使用sqlDataReader一次从查询片段中读取数据,以便我有一个“循环”来检查可以通过按钮从GUI设置的标志.问题是我知道我不能使用一个datatable的Load()方法,仍然可以取消sqlcommand.如果我错了,请让我知道,因为这会使取消稍微更容易一些.

鉴于我发现我来到这个实现,我可能只能取消sqlcommand中间查询,如果我做了类似下面的(伪代码):

@H_419_2@while(reader.Read()){ //check flag status //if it is set to ‘kill’ fire off the kill thread //otherwise populate the datatable with what was read}

然而,在我看来,这将是非常无效且可能是昂贵的.这是杀死正在进行的绝对需要在datatable中的sqlcommand的唯一方法吗?任何帮助将不胜感激!

解决方法@H_502_22@ 取消事情真的有两个阶段:

>在返回第一行之前取消初始查询执行
>中止在提供行时阅读行的过程

根据实际的sql语句的性质,这些步骤中的任何一个可能是99%的时间,所以它们都应该被考虑.例如,在具有十亿行的某些表上调用SELECT *将完全没有时间执行,但需要很长时间才能读取.相反,在调优的表上请求超级复杂的连接,然后将其全部包含在某些聚合子句中可能需要几分钟的时间才能执行,但是在实际返回后,读取少数行的时间可以忽略不计.

精心设计的高级数据库引擎还将一次缓存大量行的复杂查询,因此您将看到交替暂停,引擎在下一批行中执行查询,然后快速突发数据返回下一批的结果.

取消查询执行

为了能够在执行时取消查询,您可以使用SqlCommand.BeginExecuteReader的一个重载启动查询,并调用SqlCommand.Cancel中止它.或者,您可以在一个线程中同时调用ExecuteReader(),仍然从另一个线程调用Cancel().我不包括代码示例,因为文档中有很多.

中止读取操作

这里使用简单的布尔标志可能是最简单的方法.并且记住使用需要一个对象数组的Rows.Add()重载来填充数据表行是非常简单的,即:

@H_419_2@object[] buffer = new object[reader.FieldCount]while(reader.Read()) { if(cancelFlag) break; reader.GetValues(buffer); dataTable.Rows.Add(buffer);}

取消对Read()的阻止调用

当前面提到的一个调用reader.Read()使数据库引擎进行另一批密集处理时,会出现一种混合的情况.如MSDN文档中所述,即使原始查询使用BeginExecuteReader执行,在这种情况下,对Read()的调用也可能被阻止.您仍然可以通过在一个线程中调用Read()来处理所有读取,但在另一个线程中调用Cancel().如果读者在阻止读取调用中知道的方式是在监视线程读取时,读者线程会更新一个标志:

@H_419_2@…inRead = truewhile(reader.Read()) { inRead = false … inRead = true}// Somewhere else:private void foo_onUITimerTick(…) { status.Text = inRead ? “Waiting for server” : “Reading”;}

关于Reader vs Adapter的性能

DataReader通常比使用DataAdapter.Fill()更快. DataReader的全部要点是真正的,快速响应阅读.每行检查一些布尔标志一次,即使数百万行也不会增加可测量的差异.

大数据库查询的限制因素不是本地cpu处理时间,而是I / O管道的大小(远程数据库的网络连接或本地磁盘速度)或数据库服务器自身磁盘的组合速度和cpu处理时间的复杂查询. DataAdapter和DataReader都将花费时间(也许大部分时间)只需等待几纳秒,以便下一行被提供.

DataAdapter.Fill()的一个方便之处在于,它能够动态生成DataTable列以匹配查询结果,但这并不难做到(参见SqlDataReader.GetSchemaTable()).