
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_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_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 RsConn.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服务器端游标默认通常返回 -1adUseClient3客户端游标返回正确行数默认情况下如果你用Set RsConn.Execute(SqlStr)这种写法ADODB 会使用服务器端游标而且游标类型是只进的。服务器端游标的优点是省内存、快缺点是很多属性不支持RecordCount就是其中之一。当你把CursorLocation改成adUseClient之后ADODB 会把整个结果集拉到客户端内存里这时候它就能知道总共有多少行了。再看CursorType。它有四个值常量值说明RecordCountadOpenForwardOnly0只进游标默认-1adOpenKeyset1键集游标正确行数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 ProviderSQLOLEDB;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 ProviderSQLOLEDB;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我设成了1adLockReadOnly。如果你不需要修改数据强烈建议用只读锁这样可以避免不必要的锁竞争尤其是在高并发的老系统里。如果你确实需要更新数据可以改成3adLockOptimistic但RecordCount的行为不受LockType影响只跟CursorLocation和CursorType有关。另外如果你用的是Set RsConn.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 ProviderSQLOLEDB;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 ProviderSQLOLEDB;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 IfadApproxPosition的值是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设为adUseClient3把CursorType设为adOpenStatic3并且用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 关掉避免连接泄漏。这个习惯一旦养成以后遇到类似问题就能少走很多弯路。