
1. 為什么你的 Recordset.RecordCount 永遠(yuǎn)是 -1如果你在用 ASP、VB6 或者 VBScript 操作 ADO取rs.RecordCount時(shí)發(fā)現(xiàn)它恒等于 -1rs.PageCount也是 -1分頁直接沒法做——這不是你的代碼寫錯了而是 ADO 游標(biāo)機(jī)制在按設(shè)計(jì)行事。RecordCount 返回 -1 的本質(zhì)含義是當(dāng)前這個(gè) Recordset 使用的游標(biāo)類型不支持行數(shù)統(tǒng)計(jì)提供程序無法提前知道總共有多少條記錄。很多人第一反應(yīng)是繞過去比如再發(fā)一條select count(*) from table或者用do while not rs.EOF循環(huán)累加計(jì)數(shù)。這兩種辦法在只需要一個(gè)總數(shù)時(shí)能湊合但一旦你要用AbsolutePage、PageCount、PageSize做分頁循環(huán)計(jì)數(shù)就徹底失效了因?yàn)槟銢]法在只向前游標(biāo)上跳頁。真正要解決的是游標(biāo)配置問題而不是繞過它。RecordCount 能不能用取決于兩個(gè)屬性CursorType游標(biāo)類型和CursorLocation游標(biāo)位置。默認(rèn)情況下 ADO 用的是只向前游標(biāo)adOpenForwardOnly這種游標(biāo)為了性能只給你一條一條往下讀不維護(hù)總行數(shù)所以 RecordCount 只能是 -1。把游標(biāo)換成靜態(tài)游標(biāo)adOpenStatic或鍵集游標(biāo)adOpenKeysetRecordCount 就能返回真實(shí)行數(shù)。而 CursorLocation 決定了游標(biāo)是服務(wù)器端算還是客戶端算客戶端游標(biāo)adUseClient幾乎總是支持 RecordCount。這篇文章會從連接字符串、游標(biāo)參數(shù)、最小驗(yàn)證代碼三個(gè)層面給你一套可以直接復(fù)制運(yùn)行的排查骨架幫你確認(rèn)到底是游標(biāo)類型選錯了還是提供程序本身不支持行數(shù)統(tǒng)計(jì)。2. 前置準(zhǔn)備連接字符串與 ADO 常量在動手改游標(biāo)之前先把連接和常量理順。ADO 里那些數(shù)字參數(shù)0、1、2、3非常容易記混建議直接用 ADO 常量代碼可讀性會好很多。在 ASP 或 VB6 里你需要引用Microsoft ActiveX Data Objects 2.x Library然后就能用adOpenStatic、adUseClient這類常量。下面是一個(gè)標(biāo)準(zhǔn)的 Access 連接字符串骨架注意Provider和Data Source的寫法 Access 2003/2007 (.mdb) 連接字符串 connStr ProviderMicrosoft.Jet.OLEDB.4.0; _ Data Source Server.MapPath(data.mdb) ; _ Persist Security InfoFalse; 如果數(shù)據(jù)庫帶密碼追加 Jet OLEDB:Database Password connStr connStr Jet OLEDB:Database Passwordyourpwd;如果是 SQL Server連接字符串換成 OLE DB 提供程序connStr ProviderSQLOLEDB.1; _ Data Source127.0.0.1; _ Initial CatalogTestDB; _ User IDsa;Passwordyourpwd;連接建立之后不要急著OpenRecordset。先把游標(biāo)位置定下來再決定游標(biāo)類型順序很重要因?yàn)镃ursorLocation必須在 Recordset 打開之前設(shè)置打開之后再改是無效的。注意CursorLocation和CursorType都是 Recordset 打開前可讀寫、打開后只讀的屬性。很多人把rs.CursorLocation adUseClient寫在rs.Open后面結(jié)果當(dāng)然不生效RecordCount 還是 -1。3. 可復(fù)制配置CursorType 與 CursorLocation 組合骨架這一節(jié)給你一套可以直接抄的配置骨架。核心思路是先設(shè) CursorLocation再設(shè) CursorType最后 Open。下面這段代碼同時(shí)適用于 ASP 和 VB6Dim conn, rs Set conn Server.CreateObject(ADODB.Connection) conn.Open connStr Set rs Server.CreateObject(ADODB.Recordset) 第一步設(shè)置游標(biāo)位置為客戶端 rs.CursorLocation adUseClient 值 3 第二步設(shè)置游標(biāo)類型為靜態(tài)游標(biāo) rs.CursorType adOpenStatic 值 3 第三步打開記錄集鎖類型用只讀即可 rs.Open SELECT * FROM Products, conn, adOpenStatic, adLockReadOnly 此時(shí) RecordCount 應(yīng)該返回真實(shí)行數(shù) Response.Write 總記錄數(shù) rs.RecordCount如果你習(xí)慣用rs.Open sql, conn, 游標(biāo)類型, 鎖類型這種四參數(shù)寫法等價(jià)配置是rs.CursorLocation adUseClient rs.Open SELECT * FROM Products, conn, 3, 1 參數(shù)3adOpenStatic參數(shù)4adLockReadOnly這里有個(gè)關(guān)鍵點(diǎn)當(dāng)CursorLocation adUseClient時(shí)ADO 只支持adOpenStatic。就算你傳了adOpenDynamic提供程序也不會報(bào)錯而是悄悄用最接近的靜態(tài)游標(biāo)替代。所以客戶端游標(biāo) 靜態(tài)游標(biāo)是最穩(wěn)的組合RecordCount 和 PageCount 都能正常返回。下面這張表幫你快速對照各游標(biāo)類型對 RecordCount 的支持情況CursorType常量值RecordCount 支持適用場景adOpenForwardOnly0返回 -1單向遍歷性能最高adOpenKeyset1返回真實(shí)計(jì)數(shù)需要看到他人更新adOpenDynamic2取決于提供程序完全動態(tài)開銷大adOpenStatic3返回真實(shí)計(jì)數(shù)分頁、報(bào)表、只讀展示鎖類型LockType不影響 RecordCount它只決定記錄集能不能改。分頁場景用adLockReadOnly值 1就夠了別為了“可寫”去選悲觀鎖那會拖慢打開速度。4. 最小驗(yàn)證代碼確認(rèn)游標(biāo)是否支持行數(shù)統(tǒng)計(jì)光看配置還不夠你需要一段能跑起來、能打印結(jié)果的驗(yàn)證代碼確認(rèn)當(dāng)前游標(biāo)到底支不支持 RecordCount。下面這段 ASP 代碼可以直接放到 IIS 里跑% Dim conn, rs Set conn Server.CreateObject(ADODB.Connection) conn.Open ProviderMicrosoft.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是正數(shù)說明游標(biāo)配置成功。如果還是 -1把CursorLocation改成adUseServer再試一次對比兩次輸出。服務(wù)器端游標(biāo)下adOpenStatic通常也能返回計(jì)數(shù)但某些 OLE DB 提供程序尤其是老版本的 Jet 或第三方驅(qū)動在服務(wù)器端就是不支持這時(shí)只能靠客戶端游標(biāo)兜底。再給一個(gè) VB6 的驗(yàn)證片段邏輯一樣只是輸出用Debug.PrintDim rs As ADODB.Recordset Set rs New ADODB.Recordset rs.CursorLocation adUseClient rs.Open SELECT ProductID FROM Products, _ ProviderMicrosoft.Jet.OLEDB.4.0;Data SourceD:\nwind.mdb, _ adOpenStatic, adLockReadOnly Debug.Print RecordCount rs.RecordCount實(shí)測下來客戶端靜態(tài)游標(biāo)在 Access 和 SQL Server 上都能穩(wěn)定返回 RecordCount。唯一要注意的是客戶端游標(biāo)會把整個(gè)結(jié)果集拉到本地內(nèi)存數(shù)據(jù)量特別大幾十萬行以上時(shí)內(nèi)存占用會明顯上升這時(shí)候分頁查詢最好在 SQL 層用TOP或ROW_NUMBER()限制返回行數(shù)而不是靠 ADO 游標(biāo)硬扛。5. 本篇常見錯排查RecordCount 為 -1 的排查按下面這個(gè)順序走基本能定位到根因。錯誤一CursorLocation 寫在 Open 之后。這是最高頻的坑。rs.Open之后再設(shè)rs.CursorLocation不會報(bào)錯但也不生效RecordCount 照樣 -1。正確順序永遠(yuǎn)是先設(shè)屬性、再 Open。錯誤二用了 adOpenForwardOnly 卻想要 RecordCount。只向前游標(biāo)按設(shè)計(jì)就不維護(hù)行數(shù)這不是 bug。要計(jì)數(shù)就換adOpenStatic或adOpenKeyset。錯誤三提供程序不支持所選游標(biāo)類型。某些 OLE DB 驅(qū)動比如部分 MySQL、Oracle 的舊版驅(qū)動不支持adOpenStatic你設(shè)了它也會被替換成最接近的類型結(jié)果 RecordCount 還是 -1。排查辦法是打開后讀一下rs.CursorType的實(shí)際值看是不是被改了。錯誤四LockType 和 CursorType 沖突導(dǎo)致游標(biāo)被強(qiáng)制替換。比如adOpenStatic配adLockBatchOptimistic在某些提供程序上會觸發(fā)游標(biāo)降級。分頁只讀場景統(tǒng)一用adLockReadOnly最省事。錯誤五把 RecordCount 當(dāng)空判斷。判斷記錄集是否為空用rs.BOF And rs.EOF就夠了不需要 RecordCount。這個(gè)習(xí)慣能幫你避開很多游標(biāo)限制。提示如果你只是要判斷有沒有數(shù)據(jù)If rs.EOF Then比If rs.RecordCount 0 Then更快也更可靠后者在只向前游標(biāo)下永遠(yuǎn)是 -1判斷邏輯直接失效。排查時(shí)建議加一行調(diào)試輸出把rs.CursorLocation、rs.CursorType、rs.RecordCount三個(gè)值一起打印出來對照本文第 3 節(jié)的表格一眼就能看出是哪個(gè)環(huán)節(jié)被替換了。6. 接入與驗(yàn)證用 TaoToken 快速驗(yàn)證模型側(cè)邏輯游標(biāo)配置調(diào)通之后如果你還想把數(shù)據(jù)查詢邏輯接到大模型側(cè)做進(jìn)一步驗(yàn)證比如讓模型幫你生成分頁 SQL 或檢查游標(biāo)參數(shù)可以用 TaoToken 的模型對話能力快速試。它支持在瀏覽器里直接對話適合驗(yàn)證 SQL 生成、參數(shù)解釋這類輕量任務(wù)。需要長期在編碼工具里用的話可以看 Coding Plan把模型接入到日常開發(fā)流程里寫 ADO 分頁代碼時(shí)讓模型幫你補(bǔ)全游標(biāo)參數(shù)。API 接入地址是 https://taotoken.net/api密鑰在控制臺創(chuàng)建具體參數(shù)參考接入文檔。模型對話入口在 https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentmodel_chatAPI Keys 管理在 https://taotoken.net/console/api-keys?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentapi_keys。回到 ADO 本身最后再強(qiáng)調(diào)一個(gè)實(shí)用技巧分頁場景下與其依賴AbsolutePage在客戶端游標(biāo)上跳頁不如在 SQL 層用ROW_NUMBER()或LIMIT/OFFSET直接取當(dāng)前頁數(shù)據(jù)RecordCount 單獨(dú)用一條COUNT(*)查。這樣既避免了客戶端游標(biāo)的內(nèi)存開銷也不受提供程序游標(biāo)支持的限制是數(shù)據(jù)量大時(shí)更穩(wěn)的做法。