☰
ORACLE游标循环实战:用TaoToken统一Key跑通PL/SQL批量处理
2026/10/8 6:25:05 网站建设 项目流程

1. 从一次批量更新卡死说起:ORACLE 游标循环到底该怎么写

ORACLE 游标循环是 PL/SQL 里处理批量数据最常用的手段,简单说就是让 SQL 查询结果集像流水线一样一行行(或一批批)交给程序处理。它适合谁?适合每天要跑对账、批量更新状态、清洗历史数据的后端和 DBA,尤其是那种「几百万行表要逐条算逻辑再写回」的场景。我见过太多人第一次写游标,要么忘了exit when导致死循环,要么在循环里逐行update把库拖垮,最后只能 kill session。

这篇聚焦 ORACLE 游标循环在 PL/SQL 批量数据处理中的落地:从显式游标 FOR LOOP 到 BULK COLLECT,再结合 TaoToken 统一 Key/API 通道管理调用凭证。为什么要把游标和 TaoToken 放一起?因为现在很多批量任务不只是纯数据库操作,还要在循环里调用大模型做字段补全、文本分类、地址标准化。如果每个脚本都硬编码一个 Key,凭证散落各处,换一次 Key 要改十几个文件。用 TaoToken 把调用凭证统一管起来,游标循环里只管发请求,Key 和通道交给一个入口。

先明确三种游标循环的适用边界,这是后面所有模板的基础:

方式写法特征适用场景主要风险
LOOP + FETCH手动 open/fetch/close需要精细控制、分批提交漏写 exit when 死循环
WHILE + FETCH先 fetch 一次再 while逻辑判断在循环条件里漏写第二个 fetch 死循环
FOR LOOPfor r in cur loop绝大多数只读遍历隐式游标,无法中途改查询
BULK COLLECTfetch ... bulk collect into大批量、要限流提交集合内存占用需评估

我试过在一个 800 万行的用户表上做标签回填,最初用 FOR LOOP 逐行 update,跑了 40 分钟还没结束,后来改成 BULK COLLECT 每 1000 行提交一次,压到 3 分钟内。差别就在「逐行往返」和「批量往返」。所以这篇不会只给你语法,而是给你能直接复制、能核对结果的完整模板。

核心检索词先摆出来:ORACLE 游标循环、PL/SQL 批量处理、BULK COLLECT、TaoToken 统一 Key。你如果是来找「游标循环怎么写不死循环」或者「批量提交怎么配」的,往下看步骤就行。

2. TaoToken 前置准备:统一 Key 与 API 通道管理

在游标循环里调用外部模型之前,先把凭证这件事理顺。TaoToken 在这里扮演的角色是统一入口:你不需要在 PL/SQL 里散落多个厂商的 Key,而是通过一个 Base URL 和一个 Key 走所有模型请求。官网入口是 https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,API 地址是 https://taotoken.net/api ,注意 API 地址不带 UTM 参数,配置时别把跟踪参数拼进去。

为什么批量任务特别需要这个?因为 PL/SQL 里发 HTTP 请求本身就不算优雅,如果再叠加多套凭证管理,维护成本会爆炸。统一 Key 之后,游标循环里的调用逻辑只关心「传什么、拿什么」,不关心「用哪个 Key」。换 Key 只改一处,所有存储过程、定时任务、脚本全部生效。

前置准备分三步,都是可复制的:

第一步,拿到 Key。进入控制台创建 API Key,路径是 https://taotoken.net/console?utm_source=taotoken_aicg_blog_end&utm_content=console&utm_campaign=rewrite ,在 API Keys 页面生成。生成后立刻复制保存,页面通常只完整显示一次。API Keys 直达: https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api-keys&utm_campaign=rewrite 。

第二步,确认你要用的模型 ID。不同任务用不同模型,批量分类可以用轻量模型,复杂推理用强模型。模型对话页面可以先手动验证一次: https://taotoken.net/chat?utm_source=taotoken_aicg_blog_end&utm_content=chat&utm_campaign=rewrite 。在这里发一条测试消息,确认 Key 和通道都通,再去写 PL/SQL。

第三步,决定调用方式。PL/SQL 里发 HTTP 一般用UTL_HTTP或APEX_WEB_SERVICE。如果你用的是 Oracle APEX 环境,APEX_WEB_SERVICE.MAKE_REST_REQUEST更省事;纯数据库环境用UTL_HTTP配DBMS_LOB处理返回体。两种方式下面都会给模板。

这里要提醒一个坑:数据库服务器要能访问外网,且需要配置 ACL(访问控制列表)。Oracle 12c 以后默认禁止网络访问,必须显式授权。授权语句模板:

BEGIN DBMS_NETWORK_ACL_ADMIN.APPEND_HOST_ACE( host => 'taotoken.net', ace => xs$ace_type( privilege_list => xs$name_list('http', 'http_proxy'), principal_name => 'YOUR_DB_USER', principal_type => xs_acl.ptype_db ) ); COMMIT; END; /

把YOUR_DB_USER换成你实际执行存储过程的用户。这一步不做,后面所有 HTTP 调用都会报ORA-24247: network access denied by access control list。这个报错在排障章节还会再提。

关于长期编码和 Agent 场景,如果你是要把游标批量任务做成常态化流水线,可以了解 Coding Plan: https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding-plan&utm_campaign=rewrite 。接入文档在 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite ,配置细节以文档为准。

3. 可复制配置:游标循环模板与批量提交参数

这一节是全文的技术核心,给你三套能直接跑的模板,外加 TaoToken 调用的配置片段。所有模板都基于一张示例表users,字段id、name、status,你可以替换成自己的表。

3.1 显式游标 FOR LOOP 模板(最稳,推荐首选)

FOR LOOP 的最大好处是自动 open/fetch/close,不会死循环,也不会忘记关游标。适合只读遍历加逻辑处理:

CREATE OR REPLACE PROCEDURE proc_cursor_for AS CURSOR cur IS SELECT id, name FROM users WHERE status = 'PENDING'; BEGIN FOR r IN cur LOOP -- 这里写你的业务逻辑,r.id / r.name 直接可用 DBMS_OUTPUT.PUT_LINE(r.id || '-' || r.name); END LOOP; EXCEPTION WHEN OTHERS THEN ROLLBACK; RAISE; END; /

注意EXCEPTION里我加了RAISE,把异常继续往上抛,方便定时任务捕获。原示例只ROLLBACK不抛,问题会被吞掉,排查时很痛苦。

3.2 LOOP + FETCH 模板(需要精细控制时用)

当你需要在循环中途根据条件exit,或者要手动控制提交节奏时,用这个:

CREATE OR REPLACE PROCEDURE proc_cursor_loop AS CURSOR cur IS SELECT id, name FROM users WHERE status = 'PENDING'; v_id users.id%TYPE; v_name users.name%TYPE; v_cnt PLS_INTEGER := 0; BEGIN OPEN cur; LOOP FETCH cur INTO v_id, v_name; EXIT WHEN cur%NOTFOUND; -- 这行绝对不能少 DBMS_OUTPUT.PUT_LINE(v_id || '-' || v_name); v_cnt := v_cnt + 1; IF MOD(v_cnt, 1000) = 0 THEN COMMIT; -- 每 1000 行提交一次 END IF; END LOOP; CLOSE cur; COMMIT; EXCEPTION WHEN OTHERS THEN IF cur%ISOPEN THEN CLOSE cur; END IF; ROLLBACK; RAISE; END; /

EXIT WHEN cur%NOTFOUND是防死循环的命门。漏了它,FETCH到末尾后变量保持最后一行值,循环永远不退出。

3.3 BULK COLLECT 模板(大批量首选)

BULK COLLECT 一次取一批到集合,减少上下文切换。配合LIMIT控制每批大小,避免 PGA 内存爆掉:

CREATE OR REPLACE PROCEDURE proc_cursor_bulk AS CURSOR cur IS SELECT id, name FROM users WHERE status = 'PENDING'; TYPE t_id IS TABLE OF users.id%TYPE; TYPE t_name IS TABLE OF users.name%TYPE; v_ids t_id; v_names t_name; v_limit PLS_INTEGER := 1000; BEGIN OPEN cur; LOOP FETCH cur BULK COLLECT INTO v_ids, v_names LIMIT v_limit; EXIT WHEN v_ids.COUNT = 0; FOR i IN 1 .. v_ids.COUNT LOOP DBMS_OUTPUT.PUT_LINE(v_ids(i) || '-' || v_names(i)); END LOOP; COMMIT; -- 每批提交 END LOOP; CLOSE cur; EXCEPTION WHEN OTHERS THEN IF cur%ISOPEN THEN CLOSE cur; END IF; ROLLBACK; RAISE; END; /

LIMIT 1000是经验值,一般 500 到 5000 之间。太小提交频繁,太大内存吃紧。你可以根据行宽调整。

3.4 TaoToken 调用配置片段

在游标循环里调用模型,核心是把 Base URL、Key、Model ID 三件套配好。下面是一个 JSON 配置片段,放在应用侧或配置表里,PL/SQL 读取后拼请求:

{ "base_url": "https://taotoken.net/api", "api_key": "sk-你的TaoToken密钥", "model_id": "你的模型ID", "timeout_ms": 30000, "max_retries": 2 }

如果你用 APEX,可以在APEX_WEB_SERVICE.MAKE_REST_REQUEST里直接引用:

DECLARE v_clob CLOB; BEGIN v_clob := APEX_WEB_SERVICE.MAKE_REST_REQUEST( p_url => 'https://taotoken.net/api/v1/chat/completions', p_http_method => 'POST', p_username => NULL, p_password => 'sk-你的TaoToken密钥', p_body => '{"model":"你的模型ID","messages":[{"role":"user","content":"测试"}]}' ); DBMS_OUTPUT.PUT_LINE(DBMS_LOB.SUBSTR(v_clob, 500, 1)); END; /

注意p_password传 Key,p_username留空。请求体里的model换成你在模型对话页面验证过的 ID。这套配置和 Claude Code 接入时的三件套逻辑一致:Base URL 指向https://taotoken.net/api,Key 用生成的密钥,Model ID 用实际模型名。Claude Code 相关接入可参考 https://taotoken.net/claude-code-anthropic?utm_source=taotoken_aicg_blog_end&utm_content=claudecode&utm_campaign=rewrite 。

4. 验证请求与执行计划核对:跑通并确认结果

模板写完不能直接上生产,先验证两件事:请求通不通,执行计划对不对。

4.1 验证 TaoToken 请求

先用最简单的匿名块发一次请求,确认网络和凭证没问题:

SET SERVEROUTPUT ON SIZE UNLIMITED; DECLARE v_req UTL_HTTP.REQ; v_resp UTL_HTTP.RESP; v_body CLOB; v_text VARCHAR2(32767); BEGIN UTL_HTTP.SET_TRANSFER_TIMEOUT(30); v_req := UTL_HTTP.BEGIN_REQUEST('https://taotoken.net/api/v1/chat/completions', 'POST'); UTL_HTTP.SET_HEADER(v_req, 'Content-Type', 'application/json'); UTL_HTTP.SET_HEADER(v_req, 'Authorization', 'Bearer sk-你的TaoToken密钥'); UTL_HTTP.WRITE_TEXT(v_req, '{"model":"你的模型ID","messages":[{"role":"user","content":"ping"}]}'); v_resp := UTL_HTTP.GET_RESPONSE(v_req); DBMS_OUTPUT.PUT_LINE('HTTP Status: ' || v_resp.status_code); BEGIN LOOP UTL_HTTP.READ_TEXT(v_resp, v_text, 32767); v_body := v_body || v_text; END LOOP; EXCEPTION WHEN UTL_HTTP.END_OF_BODY THEN NULL; END; UTL_HTTP.END_RESPONSE(v_resp); DBMS_OUTPUT.PUT_LINE(DBMS_LOB.SUBSTR(v_body, 1000, 1)); END; /

成功时你会看到HTTP Status: 200和一段 JSON 返回。如果状态码是 401,说明 Key 不对或没带Bearer前缀;如果是 403,多半是 ACL 没配。

4.2 验证游标循环结果

跑完存储过程后,用对照查询核对处理行数:

-- 处理前统计 SELECT COUNT(*) FROM users WHERE status = 'PENDING'; -- 执行存储过程 BEGIN proc_cursor_bulk; END; / -- 处理后统计,确认状态已更新 SELECT COUNT(*) FROM users WHERE status = 'PENDING'; SELECT COUNT(*) FROM users WHERE status = 'DONE';

两个数字加起来应该等于总行数,否则说明有行被漏处理或重复处理。

4.3 执行计划验证

游标循环慢,十有八九是查询本身没走索引。用EXPLAIN PLAN看:

EXPLAIN PLAN FOR SELECT id, name FROM users WHERE status = 'PENDING'; SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY);

重点看TABLE ACCESS是FULL还是BY INDEX ROWID。如果status列没索引,全表扫描在百万行表上会拖垮整个循环。加索引:

CREATE INDEX idx_users_status ON users(status);

加完再跑一次EXPLAIN PLAN,确认变成索引扫描。这一步做完,BULK COLLECT 的提速效果才明显。

4.4 批量提交参数核对

提交频率直接影响 undo 表空间和性能。用下面查询观察 undo 使用:

SELECT begin_time, undoblks, maxquerylen FROM v$undostat ORDER BY begin_time DESC FETCH FIRST 5 ROWS ONLY;

如果undoblks飙升,说明单次提交太大,把LIMIT或提交间隔调小。一般每 1000 到 5000 行提交一次比较稳。

5. 本篇常见错排查:401、ACL、死循环与 OAuth

这一节对照真实报错,给你定位思路。

ORA-24247: network access denied by access control list这是最常见的第一个拦路虎。原因:数据库没授权访问taotoken.net。解决:执行第 2 节的DBMS_NETWORK_ACL_ADMIN.APPEND_HOST_ACE,把主机名和数据库用户换成实际的。执行后可能需要等几秒生效,或者COMMIT后重连会话。

HTTP 401 UnauthorizedKey 错误或格式不对。检查三点:Key 是否完整复制(有没有漏字符)、请求头是否是Authorization: Bearer sk-xxx(Bearer 后面有空格)、Key 是否已过期或被删除。去 API Keys 页面重新生成一个再试: https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api-keys&utm_campaign=rewrite 。

local proxy failed / connection refused数据库服务器到taotoken.net的网络不通。先在数据库主机上用curl或telnet测连通性。如果主机能通但数据库不通,还是 ACL 问题;如果主机都不通,检查防火墙和 DNS。

reading choices 相关报错这类通常出现在解析返回 JSON 时字段路径不对。返回体结构是choices[0].message.content,如果你按别的路径取就会报错。先用第 4.1 节的匿名块把原始返回打出来,确认结构再写解析逻辑。

OAuth 相关报错如果你在配置 Claude Code 或其他工具时遇到 OAuth 报错,多半是认证方式选错了。TaoToken 的 API 调用用 Key 认证,不需要走 OAuth 流程。Claude Code 接入时按文档配置 Base URL、Key、Model ID 三件套即可,参考 https://taotoken.net/claude-code-anthropic?utm_source=taotoken_aicg_blog_end&utm_content=claudecode&utm_campaign=rewrite 。

死循环(循环不退出)症状:存储过程一直跑,DBMS_OUTPUT刷屏。原因:LOOP 方式漏了EXIT WHEN cur%NOTFOUND,或 WHILE 方式漏了第二个FETCH。解决:对照第 3 节的模板逐行检查。WHILE 方式记住「进循环前 fetch 一次,循环体末尾再 fetch 一次」。

ORA-06502: PL/SQL: numeric or value error多半是变量长度不够。v_name定义成VARCHAR2(100)但实际数据超过 100 字符。用%TYPE让变量跟随列定义,避免硬编码长度。

BULK COLLECT 内存溢出(ORA-04030)LIMIT设太大或没设。集合一次性装太多行,PGA 撑爆。把LIMIT降到 1000 以下,或者分批处理。

排障时如果拿不准,先去模型对话页面手动发一条请求,确认通道本身没问题: https://taotoken.net/chat?utm_source=taotoken_aicg_blog_end&utm_content=chat&utm_campaign=rewrite 。通道通了再查 PL/SQL 侧。接入细节以文档为准: https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite 。

6. 把游标批量任务接上统一通道

回到实际场景:你有一张待处理表,游标循环负责遍历,TaoToken 负责在循环里提供模型能力。两者结合的关键是「凭证集中、逻辑解耦」。游标模板你直接复制第 3 节的三套,按数据量选 FOR LOOP 还是 BULK COLLECT;TaoToken 侧把 Base URL 固定为https://taotoken.net/api,Key 从控制台生成,Model ID 从模型对话页面确认。

如果你要把这套做成长期跑的流水线,比如每天定时批量清洗,建议走 Coding Plan 管理调用额度: https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding-plan&utm_campaign=rewrite 。控制台统一看用量: https://taotoken.net/console?utm_source=taotoken_aicg_blog_end&utm_content=console&utm_campaign=rewrite 。

最后给一个实用技巧:在游标循环里调用模型时,把请求结果先写进临时表,循环结束后再统一 merge 回主表。这样即使中途失败,也能从临时表断点续跑,不用从头再来。批量任务最怕的就是跑了两小时挂掉,重跑又两小时。临时表加批次号,续跑时跳过已完成的批次,这个习惯能省你很多时间。

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

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

立即咨询