☰
ADO Recordset 的 RecordCount 总是 -1?从 CursorType 与 CursorLocation 配置说起
2026/9/29 8:16:43 网站建设 项目流程

1. 为什么你的 Recordset.RecordCount 永远是 -1

如果你在用 ASP、VB6 或者 VBScript 操作 ADO,取rs.RecordCount时发现它恒等于 -1,rs.PageCount也是 -1,分页直接没法做——这不是你的代码写错了,而是 ADO 游标机制在按设计行事。RecordCount 返回 -1 的本质含义是:当前这个 Recordset 使用的游标类型不支持行数统计,提供程序无法提前知道总共有多少条记录。

很多人第一反应是绕过去,比如再发一条select count(*) from table,或者用do while not rs.EOF循环累加计数。这两种办法在只需要一个总数时能凑合,但一旦你要用AbsolutePage、PageCount、PageSize做分页,循环计数就彻底失效了,因为你没法在只向前游标上跳页。真正要解决的是游标配置问题,而不是绕过它。

RecordCount 能不能用,取决于两个属性:CursorType(游标类型)和CursorLocation(游标位置)。默认情况下 ADO 用的是只向前游标(adOpenForwardOnly),这种游标为了性能只给你一条一条往下读,不维护总行数,所以 RecordCount 只能是 -1。把游标换成静态游标(adOpenStatic)或键集游标(adOpenKeyset),RecordCount 就能返回真实行数。而 CursorLocation 决定了游标是服务器端算还是客户端算,客户端游标(adUseClient)几乎总是支持 RecordCount。

这篇文章会从连接字符串、游标参数、最小验证代码三个层面,给你一套可以直接复制运行的排查骨架,帮你确认到底是游标类型选错了,还是提供程序本身不支持行数统计。

2. 前置准备:连接字符串与 ADO 常量

在动手改游标之前,先把连接和常量理顺。ADO 里那些数字参数(0、1、2、3)非常容易记混,建议直接用 ADO 常量,代码可读性会好很多。在 ASP 或 VB6 里,你需要引用Microsoft ActiveX Data Objects 2.x Library,然后就能用adOpenStatic、adUseClient这类常量。

下面是一个标准的 Access 连接字符串骨架,注意Provider和Data Source的写法:

' Access 2003/2007 (.mdb) 连接字符串 connStr = "Provider=Microsoft.Jet.OLEDB.4.0;" & _ "Data Source=" & Server.MapPath("data.mdb") & ";" & _ "Persist Security Info=False;" ' 如果数据库带密码,追加 Jet OLEDB:Database Password connStr = connStr & "Jet OLEDB:Database Password=yourpwd;"

如果是 SQL Server,连接字符串换成 OLE DB 提供程序:

connStr = "Provider=SQLOLEDB.1;" & _ "Data Source=127.0.0.1;" & _ "Initial Catalog=TestDB;" & _ "User ID=sa;Password=yourpwd;"

连接建立之后,不要急着OpenRecordset。先把游标位置定下来,再决定游标类型,顺序很重要,因为CursorLocation必须在 Recordset 打开之前设置,打开之后再改是无效的。

注意:CursorLocation和CursorType都是 Recordset 打开前可读写、打开后只读的属性。很多人把rs.CursorLocation = adUseClient写在rs.Open后面,结果当然不生效,RecordCount 还是 -1。

3. 可复制配置:CursorType 与 CursorLocation 组合骨架

这一节给你一套可以直接抄的配置骨架。核心思路是:先设 CursorLocation,再设 CursorType,最后 Open。下面这段代码同时适用于 ASP 和 VB6:

Dim conn, rs Set conn = Server.CreateObject("ADODB.Connection") conn.Open connStr Set rs = Server.CreateObject("ADODB.Recordset") ' 第一步:设置游标位置为客户端 rs.CursorLocation = adUseClient ' 值 = 3 ' 第二步:设置游标类型为静态游标 rs.CursorType = adOpenStatic ' 值 = 3 ' 第三步:打开记录集,锁类型用只读即可 rs.Open "SELECT * FROM Products", conn, adOpenStatic, adLockReadOnly ' 此时 RecordCount 应该返回真实行数 Response.Write "总记录数:" & rs.RecordCount

如果你习惯用rs.Open sql, conn, 游标类型, 锁类型这种四参数写法,等价配置是:

rs.CursorLocation = adUseClient rs.Open "SELECT * FROM Products", conn, 3, 1 ' 参数3=adOpenStatic,参数4=adLockReadOnly

这里有个关键点:当CursorLocation = adUseClient时,ADO 只支持adOpenStatic。就算你传了adOpenDynamic,提供程序也不会报错,而是悄悄用最接近的静态游标替代。所以客户端游标 + 静态游标是最稳的组合,RecordCount 和 PageCount 都能正常返回。

下面这张表帮你快速对照各游标类型对 RecordCount 的支持情况:

CursorType常量值RecordCount 支持适用场景
adOpenForwardOnly0返回 -1单向遍历,性能最高
adOpenKeyset1返回真实计数需要看到他人更新
adOpenDynamic2取决于提供程序完全动态,开销大
adOpenStatic3返回真实计数分页、报表、只读展示

锁类型(LockType)不影响 RecordCount,它只决定记录集能不能改。分页场景用adLockReadOnly(值 1)就够了,别为了“可写”去选悲观锁,那会拖慢打开速度。

4. 最小验证代码:确认游标是否支持行数统计

光看配置还不够,你需要一段能跑起来、能打印结果的验证代码,确认当前游标到底支不支持 RecordCount。下面这段 ASP 代码可以直接放到 IIS 里跑:

<% Dim conn, rs Set conn = Server.CreateObject("ADODB.Connection") conn.Open "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" & Server.MapPath("data.mdb") Set rs = Server.CreateObject("ADODB.Recordset") rs.CursorLocation = adUseClient rs.CursorType = adOpenStatic rs.Open "SELECT * FROM Products", conn, adOpenStatic, adLockReadOnly Response.Write "CursorLocation=" & rs.CursorLocation & "<br>" Response.Write "CursorType=" & rs.CursorType & "<br>" Response.Write "RecordCount=" & rs.RecordCount & "<br>" Response.Write "PageCount=" & rs.PageCount & "<br>" Response.Write "PageSize=" & rs.PageSize & "<br>" rs.Close conn.Close Set rs = Nothing Set conn = Nothing %>

跑出来如果RecordCount是正数,说明游标配置成功。如果还是 -1,把CursorLocation改成adUseServer再试一次,对比两次输出。服务器端游标下,adOpenStatic通常也能返回计数,但某些 OLE DB 提供程序(尤其是老版本的 Jet 或第三方驱动)在服务器端就是不支持,这时只能靠客户端游标兜底。

再给一个 VB6 的验证片段,逻辑一样,只是输出用Debug.Print:

Dim rs As ADODB.Recordset Set rs = New ADODB.Recordset rs.CursorLocation = adUseClient rs.Open "SELECT ProductID FROM Products", _ "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=D:\nwind.mdb", _ adOpenStatic, adLockReadOnly Debug.Print "RecordCount=" & rs.RecordCount

实测下来,客户端静态游标在 Access 和 SQL Server 上都能稳定返回 RecordCount。唯一要注意的是客户端游标会把整个结果集拉到本地内存,数据量特别大(几十万行以上)时内存占用会明显上升,这时候分页查询最好在 SQL 层用TOP或ROW_NUMBER()限制返回行数,而不是靠 ADO 游标硬扛。

5. 本篇常见错排查

RecordCount 为 -1 的排查,按下面这个顺序走,基本能定位到根因。

错误一:CursorLocation 写在 Open 之后。这是最高频的坑。rs.Open之后再设rs.CursorLocation不会报错,但也不生效,RecordCount 照样 -1。正确顺序永远是先设属性、再 Open。

错误二:用了 adOpenForwardOnly 却想要 RecordCount。只向前游标按设计就不维护行数,这不是 bug。要计数就换adOpenStatic或adOpenKeyset。

错误三:提供程序不支持所选游标类型。某些 OLE DB 驱动(比如部分 MySQL、Oracle 的旧版驱动)不支持adOpenStatic,你设了它也会被替换成最接近的类型,结果 RecordCount 还是 -1。排查办法是打开后读一下rs.CursorType的实际值,看是不是被改了。

错误四:LockType 和 CursorType 冲突导致游标被强制替换。比如adOpenStatic配adLockBatchOptimistic在某些提供程序上会触发游标降级。分页只读场景统一用adLockReadOnly最省事。

错误五:把 RecordCount 当空判断。判断记录集是否为空,用rs.BOF And rs.EOF就够了,不需要 RecordCount。这个习惯能帮你避开很多游标限制。

提示:如果你只是要判断有没有数据,If rs.EOF Then比If rs.RecordCount = 0 Then更快也更可靠,后者在只向前游标下永远是 -1,判断逻辑直接失效。

排查时建议加一行调试输出,把rs.CursorLocation、rs.CursorType、rs.RecordCount三个值一起打印出来,对照本文第 3 节的表格,一眼就能看出是哪个环节被替换了。

6. 接入与验证:用 TaoToken 快速验证模型侧逻辑

游标配置调通之后,如果你还想把数据查询逻辑接到大模型侧做进一步验证,比如让模型帮你生成分页 SQL 或检查游标参数,可以用 TaoToken 的模型对话能力快速试。它支持在浏览器里直接对话,适合验证 SQL 生成、参数解释这类轻量任务。

需要长期在编码工具里用的话,可以看 Coding Plan,把模型接入到日常开发流程里,写 ADO 分页代码时让模型帮你补全游标参数。API 接入地址是 https://taotoken.net/api,密钥在控制台创建,具体参数参考接入文档。模型对话入口在 https://taotoken.net/models?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=model_chat,API Keys 管理在 https://taotoken.net/console/api-keys?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=api_keys。

回到 ADO 本身,最后再强调一个实用技巧:分页场景下,与其依赖AbsolutePage在客户端游标上跳页,不如在 SQL 层用ROW_NUMBER()或LIMIT/OFFSET直接取当前页数据,RecordCount 单独用一条COUNT(*)查。这样既避免了客户端游标的内存开销,也不受提供程序游标支持的限制,是数据量大时更稳的做法。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询