2

我是一名 C++ 程序员,我不熟悉 .NET 数据库模型。我通常使用IDataReader( OdbcDataReader,OledbDataReaderSqlDataReader) 从数据库中读取数据。有时当我需要使用大量数据时DataAdapter,我应该怎么做才能实现 ODBC 等本机库中存在的可滚动游标的功能?


谢谢大家的回答,但是我处于无法接受的情况,当然这是我的错,没有完全解释我的问题。我将其解释为现在已删除的答案之一中的评论。

我必须编写一个程序来充当客户端程序和 MSSQL 之间的代理,对于这个库,我有以下要求:

  • 我的程序应该兼容MSSQL2000
  • 我不知道用户将发送的所有表和查询,我应该简单地向其中添加一些信息,制作日志,......然后针对 MSSQL 执行它,因此很难使用基于查询的有序字段或表的主键(我所有的作品都在一个数据库中,但该数据库很大,可能会随着时间而改变)。
  • 客户端只需要一部分数据,大部分DBMS都支持LIMIT OFFSET,可惜MSSQL不支持,而且ROW_NUMBER不存在,MSSQL2000如果支持,那我又需要了解程序逻辑,需要解析SQL命令(其实我编写了一个解析库,boost::spirit但那是本机代码,除此之外,我还不能 100% 确定它的功能)。
  • 我可能有多个客户端,但他们发送的大多数查询是少数预定义查询之一(当然用户仍然发送自定义查询,但它约占所有查询的 30%),所以我想我可以打开一些可滚动的游标和使用该游标和自定义缓存响应客户端。
  • 服务器机器及其MSSQL将专用于我的程序,所以我真的想利用服务器和 DBMS 的所有功能来实现我的功能。

所以现在:

  • 使用可滚动游标有什么问题,为什么我应该避免它们?
  • 如何在 .NET 中使用可滚动游标?
4

4 回答 4

3

在 SQL Server 中,您可以创建分页查询。您可以从应用程序轻松处理的页码。您不需要为此任务创建游标。

对于 SQL Server 2005 或更高版本

SELECT * FROM ( SELECT *, ROW_NUMBER() OVER (ORDER BY ID) AS ROW FROM TABLEA ) AS ALIAS 
WHERE ROW > 40 
AND ROW <= 49

对于 SQL Server 2000

SELECT TOP 10 T.* FROM TABLA AS T WHERE T.ID NOT IN
    ( SELECT TOP 39 id from tabla order by id desc )
ORDER BY T.ID DESC

PD:经过编辑以包括对 SQL Server 2000 的支持

于 2012-12-31T23:11:52.890 回答
2

当 Microsoft 设计 ADO.NET API 时,他们决定只公开 firehose 游标(IDataReader等)。这实际上可能会给您带来问题,也可能不会。您说您想要“可滚动光标的功能”,但这可能意味着各种各样的事情,而不仅仅是分页,并且每个特定的用例都可以通过多种方式处理。例如:

要求:用户应该能够任意上下翻页结果集。

  • 一次只检索一页数据,例如使用ROW_NUMBER()函数。这比滚动光标更有效。

要求:我有一个非常大的数据集,我只想一次处理一行以避免内存不足。

  • 使用 ADO.NET 提供的 firehose 游标。请注意,这仅在以下情况下才实用:(a)您在循环期间根本不需要访问数据库,或者(b)您在连接字符串中配置了MARS 。
  • 通过将一组唯一标识符检索到数组中来模拟键集游标,然后遍历数组并一次读取一行数据。

要求:我正在做一个复杂的计算,涉及在结果集中向前和向后移动。

  • 您应该能够重新编写算法以消除此要求。例如,读取一组行,处理它们,读取另一组行,处理它们等。

更新(问题中提供了更多信息)

您的业​​务需求要求过高。您必须处理假定存在可滚动游标的任意查询,但您不能提供可滚动游标,并且您不能重写客户端代码以不使用可滚动游标。这是一个不可能的位置。我建议您坚持现有的(C++ 和 ODBC),不要费心尝试在 .NET 中重新编写它。

于 2013-01-03T08:29:18.650 回答
2

在不支持分页的数据库上进行分页时,我通常DataReader.Read()会跳过所有不想使用的行。

如果您不想自己构建 SQL 分页查询,您可以随意使用我的分页类:https ://github.com/jgauffin/Griffin.Data/blob/master/src/Griffin.Data/BasicLayer/Paging/ SqlServerPager.cs

于 2013-01-03T08:16:20.080 回答
2

我认为游标不适用于您的特定情况。主要原因是你有3层。但是,让我们后退两步。

大多数 3 层应用程序都有一个无状态的中间层(您的 c++ 代码)。缓存很好,因为它实际上只是一种优化,不会在中间层创建任何真实状态。中间层通常有少量打开的数据库会话。因为打开 db 会话对处理器来说代价高昂,并且在打开 db 会话后,数据库服务器会保留一定数量的 RAM。当中间层收到请求时,会处理该请求并将其交给 SQL 数据库。一种算法可用于选择任何打开的会话,甚至可以随机完成。在此模型中,无法知道哪个会话将接收下一个请求。游标属于收到原始查询请求的会话。这样你就可以'

我描述的 3 层模型主要用于 Web 应用程序,因此它们可以扩展到数百或数千个客户端。如果 SQL 服务器永远无法打开那么多会话。Microsoft ADO.NET 已经有很多特性来支持我描述的那种架构,所以实现起来并不难。根据情况,即使在非 Web 应用程序中也使用相同的方法。您可能会跟踪您的会话,以便您可以为每个客户端打开一个会话,我首先要确保用例证明了这一点。知道打开的游标也会占用大量资源。

游标仍然在单个事务中占有一席之地,只是很难让它们保持打开状态,以便客户端应用程序可以获取/更新结果集中的值。

我建议您在查询事务中执行以下操作。将查询中主表的主键值存储在单独的表中。在单独的表中包括其他值,如 sessionid 和 rownumber。通过链接到原始查询中的新表来返回前几行。在随后的调用中,只需通过链接到您的新表来再次查询相应的行。您将需要一个等效的缓存机制来清除旧数据,并根据您的需要刷新结果集。

于 2013-01-09T21:00:58.447 回答