拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

RecordCount=-1 问题排查:ADODB.RecordSet 游标与 CursorLocation 配置实战

RecordCount=-1 问题排查:ADODB.RecordSet 游标与 CursorLocation 配置实战

1. 为什么你的 RecordCount 总是返回 -1

如果你在用 ASP、VBScript 或者任何基于 ADODB 的老项目里查数据库,执行完SELECT之后想拿Rs.RecordCount看看查到了多少行,结果屏幕上赫然一个-1,别慌,这不是你的 SQL 写错了,也不是数据库连不上,而是 ADODB.RecordSet 的游标机制在“作祟”。

RecordCount返回-1是 ADODB 里最经典、最高频的“假故障”之一。它本质上不是一个错误,而是 RecordSet 在告诉你:“我现在用的这种游标模式,压根不支持向前向后随便翻,所以我没法提前告诉你总共有多少行。” 你可以把它理解成一本没有页码的书——你只能一页一页往后翻,翻到最后一页之前,谁也不知道这本书到底有多少页。

这个问题的核心,就藏在两个参数里:CursorLocation(游标位置)和CursorType(游标类型)。默认情况下,ADODB 使用的是服务器端游标(adUseServer),而服务器端游标为了节省内存和网络开销,往往采用只进只读的方式,这种模式下RecordCount自然就返回-1。很多从 ASP 时代过来的老代码,或者从网上抄来的示例代码,都没有显式设置这两个参数,于是-1就成了家常便饭。

这篇文章面向的是正在维护老系统、写 VBScript/ASP 脚本,或者用 Python 的win32com调用 ADODB 的开发者。我会从CursorLocation和CursorType两个维度,把RecordCount=-1的根因拆开讲清楚,然后给你可以直接复制粘贴的连接字符串和游标配置片段,最后附上一段最小验证代码,让你亲眼看到RecordCount从-1变成正确的行数。整个过程不需要你改数据库,也不需要装什么额外组件,改几行代码就能搞定。

我试过在一个十几年的老 ASP 项目里排查这个问题,当时分页功能一直显示“共 -1 条记录”,用户看着莫名其妙,后来发现就是CursorLocation没设对。踩过的坑不想让你再踩一遍,下面直接进入正题。

2. TaoToken 前置准备:让模型帮你快速定位游标配置问题

在动手改代码之前,我想先说一下排查思路。RecordCount=-1这个问题本身不复杂,但如果你对 ADODB 的游标体系不熟,很容易在CursorType的四个枚举值里绕晕。这时候,与其去翻十几年前的 MSDN 文档,不如直接用一个能理解代码上下文的模型来帮你分析。

我平时排查这类老技术栈的问题,会先把出错的代码片段和报错现象丢给模型,让它帮我列出可能的原因,然后再对照官方文档验证。TaoToken 在这里的作用,就是提供一个统一的 API 入口,让你可以在一个地方调用不同的模型来完成这种代码诊断。它的官网是 https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,你可以先了解一下它支持哪些模型。

具体到操作层面,你需要先拿到一个 API Key。进入控制台创建密钥的地址是 https://taotoken.net/console/api-keys ,创建好之后复制保存,后面配置请求头要用。如果你只是想先跟模型对话,验证一下它对 ADODB 游标的理解是否正确,可以直接打开模型对话页面 https://taotoken.net/model-chat ,把下面这段问题贴进去:

我在 VBScript 里用 ADODB.RecordSet 查询 SQL Server,执行完 SELECT 之后 Rs.RecordCount 返回 -1。我的代码是 Set Rs=Conn.Execute(SqlStr),没有设置 CursorLocation 和 CursorType。请问怎么改才能让 RecordCount 返回正确的行数?

模型通常会告诉你需要把CursorLocation设为adUseClient(值为 3),并且把CursorType设为adOpenStatic(值为 3)或adOpenKeyset(值为 1)。这个结论是对的,但你要理解为什么,否则换个场景又会出问题。

对于需要长期写代码、频繁调试老系统的场景,可以考虑用 Coding Plan,地址是 https://taotoken.net/coding-plan ,它更适合这种持续性的编码辅助需求。如果你用的是 Claude Code 这类工具,接入文档在 https://taotoken.net/doc ,里面有详细的 Base URL 和请求示例。

这里要强调一点:TaoToken 只是帮你调用模型的通道,它不替代你的编辑器,也不直接连你的生产数据库。你所有的代码修改和数据库操作,仍然在你自己的开发环境里完成。模型给你的建议,你需要自己判断和验证。

拿到 Key 之后,你可以用下面这个最小的请求来测试模型是否正常工作。注意,API 的基础地址是 https://taotoken.net/api ,不要加 UTM 参数。请求体里把模型 ID 换成你实际要用的,比如gpt-4o或者claude-3-5-sonnet之类的,具体支持哪些模型可以在文档里查。

curl https://taotoken.net/api/v1/chat/completions \ -H "Content-Type: application/json" \ -H "Authorization: Bearer 你的API_KEY" \ -d '{ "model": "gpt-4o", "messages": [ {"role": "user", "content": "ADODB.RecordSet 的 CursorLocation 设为 adUseClient 后,RecordCount 为什么就能返回正确行数?"} ] }'

如果返回的 JSON 里有正常的choices字段和模型回复内容,说明你的 Key 和网络都没问题。接下来就可以把实际的报错代码贴进去,让模型帮你逐行分析。这一步的意义在于,你可以快速确认自己对游标的理解是否正确,而不是盲目地改参数。

3. 可复制配置:连接字符串与游标参数完整片段

现在进入最核心的部分。要让RecordCount返回正确的行数,你需要同时控制两个东西:CursorLocation和CursorType。这两个参数决定了 RecordSet 在底层用什么方式去取数据,以及支持哪些操作。

先看CursorLocation。它有两个常用值:

常量值含义RecordCount 表现
adUseServer2服务器端游标(默认)通常返回 -1
adUseClient3客户端游标返回正确行数

默认情况下,如果你用Set Rs=Conn.Execute(SqlStr)这种写法,ADODB 会使用服务器端游标,而且游标类型是只进的。服务器端游标的优点是省内存、快,缺点是很多属性不支持,RecordCount就是其中之一。当你把CursorLocation改成adUseClient之后,ADODB 会把整个结果集拉到客户端内存里,这时候它就能知道总共有多少行了。

再看CursorType。它有四个值:

常量值说明RecordCount
adOpenForwardOnly0只进游标(默认)-1
adOpenKeyset1键集游标正确行数
adOpenDynamic2动态游标-1 或不正确
adOpenStatic3静态游标正确行数

从表里可以清楚看到,只有adOpenKeyset和adOpenStatic能保证RecordCount返回正确值。adOpenDynamic虽然听起来很厉害,但它的RecordCount取决于数据源,很多情况下还是 -1。所以最稳妥的组合是:CursorLocation = adUseClient加上CursorType = adOpenStatic。

下面是一段可以直接复制的 VBScript 代码。假设你用的是 SQL Server,连接字符串里记得换成你自己的服务器、数据库、用户名和密码。

<% Dim Conn, Rs, SqlStr ' 创建连接对象 Set Conn = Server.CreateObject("ADODB.Connection") ' 连接字符串:根据你的实际环境修改 Conn.ConnectionString = "Provider=SQLOLEDB;Data Source=你的服务器IP;Initial Catalog=你的数据库名;User ID=你的用户名;Password=你的密码;" Conn.Open ' 创建 RecordSet 对象 Set Rs = Server.CreateObject("ADODB.RecordSet") ' 关键配置:客户端游标 + 静态游标 Rs.CursorLocation = 3 ' adUseClient Rs.CursorType = 3 ' adOpenStatic Rs.LockType = 1 ' adLockReadOnly,只读,避免锁表 ' 执行查询 SqlStr = "SELECT id, name, created_at FROM users WHERE status = 1" Rs.Open SqlStr, Conn ' 现在 RecordCount 会返回正确的行数 Response.Write "共查询到 " & Rs.RecordCount & " 条记录" ' 遍历结果 Do While Not Rs.EOF Response.Write Rs("id") & " - " & Rs("name") & "<br>" Rs.MoveNext Loop ' 清理对象 Rs.Close Conn.Close Set Rs = Nothing Set Conn = Nothing %>

如果你用的是 Python 的win32com.client来调用 ADODB,配置逻辑是一样的,只是写法不同。下面是对应的 Python 片段:

import win32com.client # 创建连接 conn = win32com.client.Dispatch("ADODB.Connection") conn.ConnectionString = "Provider=SQLOLEDB;Data Source=你的服务器IP;Initial Catalog=你的数据库名;User ID=你的用户名;Password=你的密码;" conn.Open() # 创建 RecordSet rs = win32com.client.Dispatch("ADODB.RecordSet") rs.CursorLocation = 3 # adUseClient rs.CursorType = 3 # adOpenStatic rs.LockType = 1 # adLockReadOnly # 执行查询 sql = "SELECT id, name FROM users WHERE status = 1" rs.Open(sql, conn) # 读取 RecordCount print(f"共查询到 {rs.RecordCount} 条记录") # 遍历 while not rs.EOF: print(rs.Fields("id").Value, rs.Fields("name").Value) rs.MoveNext() rs.Close() conn.Close()

这里有一个细节要注意:Rs.Open的第四个参数是LockType,我设成了1(adLockReadOnly)。如果你不需要修改数据,强烈建议用只读锁,这样可以避免不必要的锁竞争,尤其是在高并发的老系统里。如果你确实需要更新数据,可以改成3(adLockOptimistic),但RecordCount的行为不受LockType影响,只跟CursorLocation和CursorType有关。

另外,如果你用的是Set Rs=Conn.Execute(SqlStr)这种简写方式,它是没有办法设置CursorLocation和CursorType的,因为Execute方法直接返回一个只进的服务器端游标 RecordSet。所以如果你需要RecordCount,就必须老老实实用Server.CreateObject("ADODB.RecordSet")然后手动Open。

还有一个容易被忽略的点:Rs.Open的第二个参数是ActiveConnection,你可以传连接对象,也可以传连接字符串。如果你传的是连接字符串,ADODB 会自己创建一个连接,但这样你就没法控制连接的生命周期了。建议还是先创建Conn,再传给Rs.Open。

4. 验证请求:最小代码演示 RecordCount 从 -1 到正确行数

光看配置还不够,我们来做一组对照实验,让你亲眼看到RecordCount的变化。下面这段代码会先故意用默认配置跑一次,打印出-1,然后再用正确配置跑一次,打印出真实行数。你可以直接复制到.asp文件里,或者用cscript跑 VBScript 版本。

<% Dim Conn, Rs, SqlStr Set Conn = Server.CreateObject("ADODB.Connection") Conn.ConnectionString = "Provider=SQLOLEDB;Data Source=你的服务器IP;Initial Catalog=你的数据库名;User ID=你的用户名;Password=你的密码;" Conn.Open SqlStr = "SELECT id, name FROM users WHERE status = 1" ' 实验一:默认配置,RecordCount 返回 -1 Set Rs = Server.CreateObject("ADODB.RecordSet") Rs.Open SqlStr, Conn Response.Write "默认配置 RecordCount = " & Rs.RecordCount & "<br>" Rs.Close ' 实验二:客户端游标 + 静态游标,RecordCount 返回正确行数 Set Rs = Server.CreateObject("ADODB.RecordSet") Rs.CursorLocation = 3 ' adUseClient Rs.CursorType = 3 ' adOpenStatic Rs.LockType = 1 ' adLockReadOnly Rs.Open SqlStr, Conn Response.Write "正确配置 RecordCount = " & Rs.RecordCount & "<br>" Rs.Close Conn.Close Set Rs = Nothing Set Conn = Nothing %>

运行之后,你会在页面上看到类似这样的输出:

默认配置 RecordCount = -1 正确配置 RecordCount = 42

这个42就是users表里status = 1的真实行数。你可以把SqlStr换成任何你想要的查询,只要结果集不是空的,RecordCount都会返回正确的数字。

如果你用的是 Python,验证代码同样简单:

import win32com.client conn = win32com.client.Dispatch("ADODB.Connection") conn.ConnectionString = "Provider=SQLOLEDB;Data Source=你的服务器IP;Initial Catalog=你的数据库名;User ID=你的用户名;Password=你的密码;" conn.Open() sql = "SELECT id, name FROM users WHERE status = 1" # 默认配置 rs1 = win32com.client.Dispatch("ADODB.RecordSet") rs1.Open(sql, conn) print(f"默认配置 RecordCount = {rs1.RecordCount}") rs1.Close() # 正确配置 rs2 = win32com.client.Dispatch("ADODB.RecordSet") rs2.CursorLocation = 3 rs2.CursorType = 3 rs2.LockType = 1 rs2.Open(sql, conn) print(f"正确配置 RecordCount = {rs2.RecordCount}") rs2.Close() conn.Close()

运行结果会跟 VBScript 版本一致。这里的关键是,你要亲眼看到-1和真实行数的对比,这样才能在以后遇到类似问题时,第一时间想到去检查CursorLocation和CursorType。

还有一个进阶技巧:你可以用Rs.Supports方法来测试当前 RecordSet 是否支持某个属性。比如:

If Rs.Supports(adApproxPosition) Then Response.Write "支持 RecordCount 和 AbsolutePosition" Else Response.Write "不支持 RecordCount" End If

adApproxPosition的值是16384。如果返回True,说明当前游标支持RecordCount;如果返回False,那RecordCount大概率是-1。这个方法可以在你动态创建 RecordSet 的时候,提前判断是否需要调整游标配置。

5. 常见报错排查:401、local proxy failed、reading choices 与 OAuth

在配置 TaoToken 和调试 ADODB 的过程中,你可能会遇到一些跟游标无关的报错。这些报错虽然不直接影响RecordCount,但会打断你的排查节奏。下面我把几个高频错误和对应的排查方法列出来。

401 Unauthorized

这个错误通常出现在你调用 TaoToken API 的时候。原因很简单:API Key 没填、填错了,或者请求头格式不对。检查你的请求头,确保是Authorization: Bearer 你的API_KEY,注意Bearer和 Key 之间有一个空格。如果你是在代码里拼接的,检查一下有没有多余的空格或换行。另外,Key 是否已经过期或被删除,可以去控制台 https://taotoken.net/console/api-keys 确认一下。

local proxy failed

这个报错一般出现在你本地网络环境有特殊配置的时候。TaoToken 的 API 地址是 https://taotoken.net/api ,如果你本地设置了系统代理,可能会导致请求被拦截。排查方法是先检查你的环境变量HTTP_PROXY和HTTPS_PROXY,如果有设置,临时取消掉再试。如果你用的是 Python 的requests库,可以显式设置proxies={"http": None, "https": None}来绕过代理。注意,这里说的是本地开发环境的代理配置问题,不是让你去用什么特殊网络工具,只是检查一下系统设置。

reading choices 报错

这个错误通常出现在你解析模型返回的 JSON 时。比如你期望返回里有choices字段,但实际返回的是一个错误对象,里面只有error字段。这时候你的代码去读response["choices"]就会报 KeyError 或者类似的错误。正确的做法是先判断返回里有没有error,如果有,先把错误信息打印出来。常见的错误信息包括invalid_api_key、model_not_found、rate_limit_exceeded等。根据错误信息去调整你的请求参数。

OAuth 相关报错

如果你用的是 Claude Code 或者其他需要 OAuth 授权的工具,可能会遇到 token 过期或者授权失败的问题。这时候你需要重新走一遍授权流程。TaoToken 的接入文档 https://taotoken.net/doc 里有详细的说明,包括 Base URL 怎么填、Model ID 怎么选。如果你用的是 Claude Code,可以参考 https://taotoken.net/claude-code 这个页面,里面有专门的配置指南。

这里要特别提醒一点:无论你用的是 CC Switch、Cline MCP 还是 Codex 的auth.json,只要涉及到接入配置,都必须同时确认三件套:Base URL、API Key、Model ID。缺一个都会导致请求失败。Base URL 统一用 https://taotoken.net/api ,不要加任何路径后缀,除非文档里明确说了要加。API Key 就是你在控制台创建的那一串字符。Model ID 要跟你实际使用的模型名称完全一致,大小写敏感。

如果你在排查RecordCount=-1的过程中,发现代码改对了但还是返回-1,那就要检查一下是不是连接字符串里的 Provider 有问题。比如你用的是MSDASQL或者SQLOLEDB,不同 Provider 对游标的支持程度不一样。SQLOLEDB对客户端游标支持得比较好,MSDASQL有时候会有兼容性问题。可以试着换成SQLOLEDB再试。

还有一个坑:如果你在Rs.Open之后又执行了Rs.Requery,有些情况下RecordCount会重新变成-1。这是因为Requery可能会重置游标状态。如果你需要重新查询,建议关掉旧的 RecordSet,重新创建一个新的,并且重新设置CursorLocation和CursorType。

6. 语义一致 CTA:继续深入 ADODB 与模型辅助调试

走到这里,你应该已经能让RecordCount正确返回行数了。核心就一句话:把CursorLocation设为adUseClient(3),把CursorType设为adOpenStatic(3),并且用Server.CreateObject("ADODB.RecordSet")手动创建 RecordSet,而不是用Conn.Execute的简写方式。

如果你在排查过程中遇到了其他 ADODB 的报错,或者想更系统地了解游标类型对分页、锁、更新操作的影响,可以继续用模型来辅助分析。把具体的报错信息和你的代码片段贴到模型对话页面 https://taotoken.net/model-chat ,让模型帮你逐行解读。对于需要长期维护老系统的场景,Coding Plan https://taotoken.net/coding-plan 可能更适合你,因为它能保持上下文,不用每次重新描述问题。

如果你需要创建新的 API Key 或者查看已有的 Key,控制台地址是 https://taotoken.net/console/api-keys 。接入文档在 https://taotoken.net/doc ,里面有完整的 Base URL、请求示例和模型列表。Claude Code 的专项配置可以参考 https://taotoken.net/claude-code 。

最后再强调一个实用技巧:在你的老项目里,如果有很多地方都用了Conn.Execute来查询并且依赖RecordCount,不要一个一个去改。你可以写一个通用的查询函数,把CursorLocation和CursorType的配置封装进去,这样以后所有查询都走这个函数,既统一又不容易出错。这个函数大概长这样:

Function QueryWithCount(SqlStr) Dim Rs Set Rs = Server.CreateObject("ADODB.RecordSet") Rs.CursorLocation = 3 Rs.CursorType = 3 Rs.LockType = 1 Rs.Open SqlStr, Conn Set QueryWithCount = Rs End Function

调用的时候直接Set Rs = QueryWithCount("SELECT ..."),然后Rs.RecordCount就能正常用了。记得在页面结束前把 Rs 关掉,避免连接泄漏。这个习惯一旦养成,以后遇到类似问题就能少走很多弯路。

返回列表