用Oracle存儲過程返回Cursor的方法:TaoToken 統(tǒng)一 Key 通道下的可復(fù)制配置與驗證)
1. Python 調(diào)用 Oracle 存儲過程返回 Cursor 的真實場景與坑點如果你正在用 Python 連 Oracle并且存儲過程里返回的是SYS_REFCURSOR那你大概率會遇到一個很典型的問題普通cursor.execute()執(zhí)行完拿不到結(jié)果集或者拿到的對象不是可迭代的行而是一個綁定變量句柄。這個場景在報表查詢、批量數(shù)據(jù)導(dǎo)出、老系統(tǒng)對接里非常常見尤其是 Oracle 存儲過程把查詢結(jié)果通過OUT參數(shù)以游標(biāo)形式吐出來的時候。我先把結(jié)論說清楚Python 的oracledb驅(qū)動也就是原來cx_Oracle的新名字調(diào)用返回 Cursor 的存儲過程核心動作只有三步——用cursor.var()聲明一個游標(biāo)類型的綁定變量、把它作為OUT參數(shù)傳進(jìn)begin ... end;匿名塊、執(zhí)行完之后直接對這個游標(biāo)變量做for row in cursor_var迭代。聽起來簡單但真正卡人的地方在于連接池怎么配、綁定變量類型怎么聲明、字段映射怎么對齊、異常怎么捕獲。這幾個點任何一個沒處理好你看到的報錯就是ORA-01036、DPI-1010、ORA-06550這類讓人頭大的信息。這篇內(nèi)容聚焦的就是這條完整鏈路從連接串配置到存儲過程調(diào)用再到游標(biāo)讀取和異常處理。我會給出可以直接復(fù)制的配置片段也會用一個最小的存儲過程來驗證 Cursor 返回是否正常。如果你本地已經(jīng)裝好了 Oracle 客戶端和 Python 環(huán)境跟著走一遍就能復(fù)現(xiàn)。另外說明一下本文里涉及模型調(diào)用通道的部分統(tǒng)一走 TaoToken 的 Key 通道來做演示目的是讓配置片段保持一致的鑒權(quán)方式方便你在同一套環(huán)境里既跑數(shù)據(jù)庫調(diào)用、又跑模型輔助排查。數(shù)據(jù)庫本身還是連你自己的 Oracle 實例TaoToken 只負(fù)責(zé)模型側(cè)的統(tǒng)一入口兩者不混。先明確幾個前置條件避免你走到一半發(fā)現(xiàn)環(huán)境不對Python 3.9 及以上推薦 3.11。安裝oracledbpip install oracledb。注意不要再用cx_Oracle新項目直接用oracledb它是官方維護(hù)的。Oracle 客戶端庫Instant Client要能被找到或者用 thin 模式。oracledb默認(rèn) thin 模式不需要 Instant Client但如果你要用某些高級特性比如特定的字符集、外部認(rèn)證還是得配lib_dir。有一個可用的 Oracle 實例里面有建存儲過程的權(quán)限。我試過在 thin 模式下直接連 Oracle 19c 和 21c基本沒問題。但如果你連的是比較老的 11gthin 模式可能不支持那就得裝 Instant Client 并設(shè)置ORACLE_CLIENT_LIB_DIR或者初始化時傳lib_dir。存儲過程這邊假設(shè)我們有一個測試用的過程簽名大概是這樣CREATE OR REPLACE PROCEDURE test1( param1 IN VARCHAR2, param2 IN VARCHAR2, p_cursor OUT SYS_REFCURSOR ) AS BEGIN OPEN p_cursor FOR SELECT a1 AS col_a, a2 AS col_b FROM dual UNION ALL SELECT b1, b2 FROM dual; END; /這個過程的邏輯很簡單接收兩個入?yún)⒋蜷_一個游標(biāo)返回兩行數(shù)據(jù)。用它來驗證 Cursor 返回是最小可復(fù)現(xiàn)的。你可以在 SQL Developer 或者 sqlplus 里先跑一遍確認(rèn)過程本身沒問題再去 Python 里調(diào)。很多人第一次調(diào)的時候會把OUT SYS_REFCURSOR當(dāng)成普通返回值處理寫成result cursor.callproc(...)結(jié)果發(fā)現(xiàn)拿不到行。這就是沒理解 Oracle 的游標(biāo)是綁定變量機(jī)制。正確做法是用cursor.var(oracledb.CURSOR)聲明一個游標(biāo)變量傳進(jìn)去執(zhí)行后迭代它。還有一個高頻坑連接池的dsn寫法。192.168.3.169:1521/orclpdb1這種是 EZConnect 格式host:port/service_name。如果你用的是 SID 而不是 service name得寫成host:port/SID或者用完整的(DESCRIPTION...)。寫錯了就是ORA-12514或者DPY-6005。下面進(jìn)入具體配置。我會先講 TaoToken 的前置準(zhǔn)備再給可復(fù)制的連接與調(diào)用代碼然后是驗證和排錯。2. TaoToken 統(tǒng)一 Key 通道的前置準(zhǔn)備與模型輔助排查這一節(jié)說清楚 TaoToken 在這套流程里扮演什么角色。你調(diào) Oracle 存儲過程本身不需要 TaoToken數(shù)據(jù)庫連接是直連你的 Oracle 實例。但實際開發(fā)中你經(jīng)常需要讓模型幫你解釋報錯、生成 SQL、檢查綁定變量寫法這時候如果每個工具都單獨配 Key會很亂。TaoToken 提供的是一個統(tǒng)一的 Key 通道把模型調(diào)用收斂到一個入口Base URL 和 Key 配一次多個工具復(fù)用。官網(wǎng)入口在這里https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 。你可以先注冊拿到 Key后面配置里會用到。拿到 Key 之后模型側(cè)的 Base URL 統(tǒng)一用https://taotoken.net/api注意這個地址不帶 UTM 參數(shù)是給程序調(diào)用的。Key 的格式一般是一串以sk-開頭的字符串具體以你控制臺里顯示的為準(zhǔn)。如果你只是想讓模型幫你排查 Oracle 報錯最直接的方式是打開模型對話頁面把報錯原文貼進(jìn)去。模型對話入口https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_contentmodel_chatutm_campaignrewrite 。這個頁面適合快速問一句“這個 ORA-01036 是什么原因”不用寫代碼。如果你是要長期做編碼、寫 Agent、跑自動化腳本那更適合用 Coding Plan。Coding Plan 入口https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding_planutm_campaignrewrite 。它的定位是給編碼場景用的套餐配合 Claude Code、Cline 這類工具比較順。Key 的管理在控制臺https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewrite 。你可以在這里創(chuàng)建、查看、吊銷 Key。API Keys 頁面https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi_keysutm_campaignrewrite 新建 Key 就在這里。接入文檔https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 里面有各語言的調(diào)用示例配環(huán)境變量、改 Base URL 的寫法都能查到。如果你用的是 Claude Code它有自己的接入方式參考https://taotoken.net/claude-code-anthropic?utm_sourcetaotoken_aicg_blog_endutm_contentclaude_codeutm_campaignrewrite 。這個頁面講的是怎么把 Claude Code 接到統(tǒng)一通道上。這里要強(qiáng)調(diào)一點TaoToken 是模型調(diào)用的統(tǒng)一入口不是數(shù)據(jù)庫代理也不是什么中轉(zhuǎn)。你的 Oracle 連接串、賬號密碼、存儲過程調(diào)用全都走你自己的網(wǎng)絡(luò)和數(shù)據(jù)庫跟 TaoToken 無關(guān)。TaoToken 只處理模型 API 的鑒權(quán)收斂。把這兩件事分清楚后面配置就不會混。前置準(zhǔn)備清單注冊并拿到 TaoToken Key。確認(rèn)模型 Base URL 是https://taotoken.net/api。本地 Python 環(huán)境裝好oracledb。Oracle 實例可連測試存儲過程已創(chuàng)建。如果要讓模型輔助排查準(zhǔn)備好模型對話或 Coding Plan 的入口。環(huán)境變量建議這樣設(shè)避免 Key 硬編碼在代碼里export TAOTOKEN_API_KEYsk-你的key export TAOTOKEN_BASE_URLhttps://taotoken.net/apiWindows 下用set或者系統(tǒng)環(huán)境變量界面設(shè)置。設(shè)完之后Python 里用os.environ.get(TAOTOKEN_API_KEY)讀取。模型側(cè)的最小驗證可以用 curl 快速確認(rèn) Key 通不通curl https://taotoken.net/api/v1/models \ -H Authorization: Bearer $TAOTOKEN_API_KEY返回模型列表就說明 Key 和 Base URL 都對。這一步不通后面模型輔助排查就別指望了。常見問題是 Key 復(fù)制時帶了空格或者 Base URL 寫成了帶 UTM 的地址。記住程序調(diào)用用https://taotoken.net/api不帶參數(shù)。數(shù)據(jù)庫側(cè)的前置確認(rèn)oracledb版本python -c import oracledb; print(oracledb.__version__)輸出類似2.5.0就正常。如果報ModuleNotFoundError回去pip install oracledb。到這里模型通道和數(shù)據(jù)庫環(huán)境都準(zhǔn)備好了。下一節(jié)進(jìn)入可復(fù)制的配置片段包括連接池、綁定變量聲明、存儲過程調(diào)用。3. 可復(fù)制的連接池與存儲過程調(diào)用配置片段這一節(jié)是全文的核心給出可以直接復(fù)制運行的代碼。我會把連接池配置、游標(biāo)變量聲明、存儲過程調(diào)用、結(jié)果讀取拆開講每一段都標(biāo)清楚路徑和參數(shù)含義。先看連接池配置。用oracledb.create_pool()創(chuàng)建池參數(shù)含義如下import oracledb pool oracledb.create_pool( userdamao, passwordwoaiwojia, dsn192.168.3.169:1521/orclpdb1, min1, max4, increment1, )user和password是你的 Oracle 賬號。dsn是 EZConnect 格式host:port/service_name。min是池啟動時的最小連接數(shù)max是最大連接數(shù)increment是每次擴(kuò)容增加的連接數(shù)。這幾個值按你實際并發(fā)調(diào)本地測試min1, max4足夠。如果你要用 SQLAlchemy可以這樣接from sqlalchemy import create_engine from sqlalchemy.pool import NullPool engine create_engine( oracleoracledb://, creatorpool.acquire, poolclassNullPool, )注意creatorpool.acquire是把池的獲取連接方法交給 SQLAlchemypoolclassNullPool是避免 SQLAlchemy 再套一層池。這個組合在需要復(fù)用 oracledb 池的場景下比較常見。接下來是調(diào)用存儲過程的關(guān)鍵部分。假設(shè)存儲過程簽名是test1(param1 IN VARCHAR2, param2 IN VARCHAR2, p_cursor OUT SYS_REFCURSOR)調(diào)用代碼如下connect pool.acquire() cursor connect.cursor() # 聲明游標(biāo)類型的綁定變量 cursor_var cursor.var(oracledb.CURSOR) cursor.execute( begin test1(:param1, :param2, :p_cursor); end; , param1a1, param2a2, p_cursorcursor_var, ) # 迭代游標(biāo)變量拿結(jié)果 for row in cursor_var: print(row , row) cursor.close() connect.close() pool.close()這里有幾個點必須說清楚第一cursor.var(oracledb.CURSOR)聲明的是游標(biāo)類型綁定變量。oracledb.CURSOR是常量對應(yīng) Oracle 的SYS_REFCURSOR。不要寫成oracledb.NUMBER或者oracledb.STRING類型不對會報DPI-1010或者ORA-06550。第二匿名塊里用:p_cursor作為占位符執(zhí)行時通過關(guān)鍵字參數(shù)p_cursorcursor_var傳入。參數(shù)名要和占位符一致順序無所謂因為是按名字綁定的。第三執(zhí)行完之后直接for row in cursor_var迭代。cursor_var本身是可迭代的每次迭代返回一行行是 tuple 或者命名元組取決于你的rowfactory設(shè)置。如果你需要拿到字段名可以這樣columns [d[0] for d in cursor_var.description] print(columns , columns) for row in cursor_var: print(dict(zip(columns, row)))cursor_var.description返回字段描述每個元素第一個是字段名。這樣就能把行映射成字典方便后續(xù)處理。如果你用的是 SQLAlchemy 的engine.connect()調(diào)用方式類似但要注意連接是從 engine 拿的with engine.connect() as conn: raw conn.connection cur raw.cursor() cur_var cur.var(oracledb.CURSOR) cur.execute( begin test1(:p1, :p2, :pc); end;, p1a1, p2a2, pccur_var, ) for row in cur_var: print(row) cur.close()conn.connection拿到的是底層 oracledb 連接這樣才能用cursor.var()。SQLAlchemy 的text()包裝層不支持游標(biāo)綁定變量所以必須下沉到原生連接。關(guān)于參數(shù)類型如果你的存儲過程入?yún)⑹菙?shù)字Python 側(cè)傳 int 或 float 就行oracledb 會自動映射。如果是日期傳datetime.datetime對象。如果是 CLOB/BLOB需要額外聲明類型這里不展開。一個完整的可運行腳本把上面拼起來import oracledb def main(): pool oracledb.create_pool( userdamao, passwordwoaiwojia, dsn192.168.3.169:1521/orclpdb1, min1, max4, increment1, ) connect pool.acquire() try: cursor connect.cursor() cursor_var cursor.var(oracledb.CURSOR) cursor.execute( begin test1(:param1, :param2, :p_cursor); end; , param1a1, param2a2, p_cursorcursor_var, ) columns [d[0] for d in cursor_var.description] print(columns , columns) for row in cursor_var: print(row , row) cursor.close() finally: connect.close() pool.close() if __name__ __main__: main()這個腳本直接跑能打印出字段名和兩行數(shù)據(jù)就說明整條鏈路通了。如果你在配置模型輔助排查比如用 Cline 或 Claude Code需要寫全三件套Base URL、Key、Model ID。以 Cline 的 MCP 配置為例JSON 片段如下{ mcpServers: { taotoken: { command: npx, args: [-y, taotoken/mcp-server], env: { TAOTOKEN_BASE_URL: https://taotoken.net/api, TAOTOKEN_API_KEY: sk-你的key, TAOTOKEN_MODEL: claude-3-5-sonnet } } } }這里TAOTOKEN_BASE_URL、TAOTOKEN_API_KEY、TAOTOKEN_MODEL就是三件套缺一不可。Model ID 按你實際可用的填控制臺里能查到。如果你用 Codex它的auth.json配置類似{ base_url: https://taotoken.net/api, api_key: sk-你的key, model: claude-3-5-sonnet }路徑一般在~/.codex/auth.json或者項目根目錄的.codex/auth.json按你的 Codex 版本為準(zhǔn)。這些配置的作用是讓模型工具能通過統(tǒng)一通道調(diào)用跟 Oracle 調(diào)用是兩條獨立的線。你排查 Oracle 報錯時可以把報錯貼給模型讓它解釋。配置片段到這里。下一節(jié)講怎么驗證請求成功、怎么確認(rèn) Cursor 返回正確。4. 驗證請求與成功結(jié)果從執(zhí)行到字段映射配置寫完之后必須驗證。驗證分兩層模型通道通不通數(shù)據(jù)庫調(diào)用通不通。先快速過模型通道再重點講數(shù)據(jù)庫。模型通道驗證前面 curl 已經(jīng)給過。再補(bǔ)一個 Python 版本import os import requests resp requests.get( https://taotoken.net/api/v1/models, headers{Authorization: fBearer {os.environ[TAOTOKEN_API_KEY]}}, timeout10, ) print(resp.status_code) print(resp.json())返回 200 和模型列表說明 Key 和 Base URL 都對。如果 401檢查 Key如果連接超時檢查網(wǎng)絡(luò)。數(shù)據(jù)庫調(diào)用驗證跑上一節(jié)的完整腳本。預(yù)期輸出columns [COL_A, COL_B] row (a1, a2) row (b1, b2)字段名是大寫因為 Oracle 默認(rèn)把未加引號的標(biāo)識符轉(zhuǎn)大寫。行是 tuple順序和description一致。如果你看到這個輸出說明 Cursor 返回、字段映射、游標(biāo)迭代都正常。如果存儲過程返回的字段有別名比如SELECT col_a AS myCol那description里就是myCol大小寫按你寫的來。這點在做字段映射時要注意別硬編碼大寫。驗證字段映射可以加一段類型檢查for d in cursor_var.description: print(name , d[0], type , d[1], size , d[3])d[1]是類型碼d[3]是顯示大小。這樣你能確認(rèn)每個字段的類型是否符合預(yù)期。比如VARCHAR2對應(yīng)oracledb.DB_TYPE_VARCHARNUMBER對應(yīng)oracledb.DB_TYPE_NUMBER。驗證多行返回把存儲過程改成返回 100 行看迭代是否完整CREATE OR REPLACE PROCEDURE test_many( p_cursor OUT SYS_REFCURSOR ) AS BEGIN OPEN p_cursor FOR SELECT LEVEL AS n, row_ || LEVEL AS label FROM dual CONNECT BY LEVEL 100; END; /Python 側(cè)cursor_var cursor.var(oracledb.CURSOR) cursor.execute(begin test_many(:pc); end;, pccursor_var) count 0 for row in cursor_var: count 1 print(total rows , count)輸出total rows 100就說明大結(jié)果集也能正常迭代。注意游標(biāo)是流式讀取的不會一次性把 100 行全加載到內(nèi)存這對大結(jié)果集友好。驗證異常處理故意傳錯參數(shù)類型try: cursor.execute( begin test1(:p1, :p2, :pc); end;, p1123, # 故意傳數(shù)字存儲過程期望 VARCHAR2 p2a2, pccursor_var, ) except oracledb.DatabaseError as e: error, e.args print(code , error.code) print(message , error.message)預(yù)期捕獲到ORA-06502或者類型轉(zhuǎn)換相關(guān)錯誤。這樣你能確認(rèn)異常捕獲路徑是通的。驗證連接池復(fù)用連續(xù)調(diào)用多次看連接是否正常歸還for i in range(5): conn pool.acquire() cur conn.cursor() cv cur.var(oracledb.CURSOR) cur.execute(begin test1(:p1, :p2, :pc); end;, p1a, p2b, pccv) rows list(cv) print(fround {i}, rows {len(rows)}) cur.close() conn.close()輸出 5 輪每輪 2 行說明池的獲取和歸還正常。如果池耗盡會卡在pool.acquire()這時候檢查max是不是太小或者有沒有連接沒關(guān)。驗證成功之后把結(jié)果和預(yù)期對照。我一般會寫一個簡單的斷言assert columns [COL_A, COL_B] assert len(rows) 2 assert rows[0] (a1, a2)斷言通過整條鏈路就算驗證完畢。這里再提一下模型輔助驗證的用法。如果你在驗證過程中遇到報錯可以把報錯和你的代碼片段一起貼到模型對話里讓它幫你定位。比如貼DPI-1010: invalid binding type模型會告訴你綁定變量類型聲明錯了。這種用法比你自己翻文檔快。驗證階段常見的結(jié)果異常輸出columns []說明游標(biāo)沒打開存儲過程可能沒執(zhí)行到OPEN檢查過程邏輯。輸出行數(shù)不對檢查存儲過程的WHERE條件或者綁定變量是否傳對。字段名亂碼檢查數(shù)據(jù)庫字符集和客戶端字符集是否一致。迭代時報DPI-1067游標(biāo)已經(jīng)被關(guān)閉或者被消費過檢查是否重復(fù)迭代。下一節(jié)專門講這些報錯的排查。5. 本篇常見報錯排查401、DPI-1010、ORA-01036 與游標(biāo)讀取異常這一節(jié)按真實報錯來對照給出原因和修復(fù)動作。我把最常見的幾類列出來你遇到哪個查哪個。401 Unauthorized模型通道報錯原文{error: {message: Invalid API key, type: invalid_request_error}}原因TaoToken Key 不對或者請求頭沒帶對。檢查Authorization: Bearer sk-xxx格式Key 前后有沒有空格。Base URL 必須是https://taotoken.net/api不要帶 UTM 參數(shù)。如果 Key 剛創(chuàng)建等幾秒再試。修復(fù)echo $TAOTOKEN_API_KEY curl https://taotoken.net/api/v1/models -H Authorization: Bearer $TAOTOKEN_API_KEYlocal proxy failed模型通道報錯原文local proxy failed: dial tcp 127.0.0.1:7890: connect: connection refused原因本地配了代理但代理沒啟動或者代理端口不對。這個報錯跟 TaoToken 無關(guān)是你本地網(wǎng)絡(luò)配置的問題。檢查環(huán)境變量HTTP_PROXY、HTTPS_PROXY或者工具自己的代理設(shè)置。修復(fù)關(guān)掉代理或者把代理指向正確的端口。如果你不需要代理直接 unsetunset HTTP_PROXY unset HTTPS_PROXYDPI-1010: invalid binding type報錯原文oracledb.exceptions.DatabaseError: DPI-1010: invalid binding type原因綁定變量類型聲明錯了。最常見的是把游標(biāo)變量聲明成了oracledb.NUMBER或oracledb.STRING。存儲過程的OUT SYS_REFCURSOR必須用cursor.var(oracledb.CURSOR)。修復(fù)# 錯誤 cursor_var cursor.var(oracledb.NUMBER) # 正確 cursor_var cursor.var(oracledb.CURSOR)ORA-01036: illegal variable name/number報錯原文oracledb.exceptions.DatabaseError: ORA-01036: illegal variable name/number原因占位符和傳入?yún)?shù)不匹配。比如匿名塊里寫了:p_cursor但執(zhí)行時傳的是cursorcursor_var名字對不上?;蛘哒嘉环麛?shù)量不對。修復(fù)占位符名字和執(zhí)行參數(shù)名嚴(yán)格一致。# 錯誤 cursor.execute(begin test1(:p1, :p2, :pc); end;, param1a, param2b, cursorcursor_var) # 正確 cursor.execute(begin test1(:p1, :p2, :pc); end;, p1a, p2b, pccursor_var)ORA-06550: PLS-00306: wrong number or types of arguments報錯原文oracledb.exceptions.DatabaseError: ORA-06550: line 1, column 7: PLS-00306: wrong number or types of arguments in call to TEST1原因調(diào)用存儲過程時參數(shù)個數(shù)或類型不對。檢查存儲過程簽名確認(rèn)入?yún)⒑统鰠?shù)量、類型匹配。比如過程需要 3 個參數(shù)你只傳了 2 個。修復(fù)對照DESC test1的輸出逐個核對參數(shù)。DESC test1;ORA-12514: TNS:listener does not currently know of service報錯原文oracledb.exceptions.DatabaseError: ORA-12514: TNS:listener does not currently know of service requested in connect descriptor原因dsn里的 service name 寫錯了。192.168.3.169:1521/orclpdb1里的orclpdb1必須是數(shù)據(jù)庫實際注冊的 service name。修復(fù)在數(shù)據(jù)庫服務(wù)器上查SELECT name FROM v$services;用查到的 service name 替換。reading choices 相關(guān)報錯模型通道報錯原文Error reading choices: unexpected end of JSON input原因模型返回的響應(yīng)不完整通常是網(wǎng)絡(luò)中斷或者超時。也可能是 Base URL 配錯返回了非 JSON 內(nèi)容。修復(fù)檢查 Base URL 是否為https://taotoken.net/api加大超時時間重試。如果持續(xù)出現(xiàn)換個模型試試。OAuth 相關(guān)報錯Claude Code 接入報錯原文OAuth token expired or invalid原因Claude Code 的鑒權(quán)過期。如果你是通過統(tǒng)一通道接入檢查配置里的 Key 是否還有效。修復(fù)重新生成 Key更新配置。Claude Code 的接入配置參考前面給的鏈接確認(rèn) Base URL、Key、Model ID 三件套都填對。游標(biāo)讀取異常DPI-1067報錯原文oracledb.exceptions.InterfaceError: DPI-1067: the cursor has already been closed原因游標(biāo)被重復(fù)迭代或者提前關(guān)閉。比如你先list(cursor_var)消費了一遍又for row in cursor_var再消費一遍第二次就報這個。修復(fù)游標(biāo)只能消費一次。需要多次使用就先轉(zhuǎn)成 listrows list(cursor_var) for row in rows: print(row) for row in rows: print(row) # 這樣沒問題游標(biāo)讀取異常結(jié)果為空但沒報錯現(xiàn)象for row in cursor_var一行都不輸出也不報錯。原因存儲過程里的OPEN沒執(zhí)行到或者WHERE條件過濾掉了所有行。也可能是游標(biāo)變量聲明了但沒傳進(jìn)過程。修復(fù)先在 SQL Developer 里直接跑存儲過程確認(rèn)有數(shù)據(jù)返回。再檢查 Python 側(cè)的綁定變量是否傳對。連接池耗盡現(xiàn)象程序卡在pool.acquire()不返回。原因連接沒歸還池的max太小或者有連接泄漏。修復(fù)確保每次pool.acquire()之后都有connect.close()。用try/finally保證歸還。臨時加大max看是否緩解。connect pool.acquire() try: # 你的操作 pass finally: connect.close()字段名大小寫不一致現(xiàn)象columns里是大寫但你代碼里按小寫取取不到。原因Oracle 默認(rèn)大寫標(biāo)識符。修復(fù)統(tǒng)一用大寫或者建表/查詢時用雙引號指定大小寫。映射時用dict(zip(columns, row))按實際字段名取。row_dict dict(zip(columns, row)) print(row_dict[COL_A])這些報錯覆蓋了大部分場景。遇到?jīng)]列出來的把報錯原文貼到模型對話里讓它幫你分析。模型對話入口前面給過這里再放一次方便你復(fù)制https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_contentmodel_chatutm_campaignrewrite 。排查的時候有個原則先確認(rèn)是模型通道的問題還是數(shù)據(jù)庫的問題。模型通道的報錯通常是 401、超時、JSON 解析失敗數(shù)據(jù)庫的報錯通常是 ORA-、DPI- 開頭。分開定位效率高很多。6. 統(tǒng)一 Key 通道下的接入與驗證入口走到這里整條鏈路應(yīng)該已經(jīng)跑通了?;仡櫼幌玛P(guān)鍵動作連接池配置用oracledb.create_pool()游標(biāo)變量用cursor.var(oracledb.CURSOR)聲明存儲過程用匿名塊調(diào)用結(jié)果直接迭代游標(biāo)變量。字段映射靠cursor_var.description異常捕獲用oracledb.DatabaseError。如果你還需要把這套流程固化到項目里建議把連接池做成單例避免每次調(diào)用都重建。Key 和連接串走環(huán)境變量不要硬編碼。存儲過程調(diào)用封裝成函數(shù)入?yún)⒑统鰠⒚鞔_。模型側(cè)的統(tǒng)一通道Key 管理在控制臺接入文檔在文檔頁。需要新建 Key 就去 API Keys 頁面。長期編碼場景用 Coding Plan快速問報錯用模型對話。這幾個入口按你的實際需求選不用全用。最后給一個實用技巧把存儲過程的調(diào)用和模型排查串起來。寫一個腳本捕獲到oracledb.DatabaseError時自動把報錯和 SQL 片段發(fā)給模型讓它返回可能的原因。這樣排查效率會高很多。模型調(diào)用走統(tǒng)一通道Base URL 和 Key 配一次就行。如果你在接入過程中卡住優(yōu)先檢查三件套Base URL 是不是https://taotoken.net/apiKey 是不是有效Model ID 是不是可用。這三個對了模型通道就通。數(shù)據(jù)庫側(cè)檢查連接串、綁定變量類型、存儲過程簽名。兩邊分開查問題很快能定位。