ARTICLE DETAIL

资讯详情

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

ORACLE游标实战:从显式游标到游标变量,一次讲透TaoToken统一Key下的数据库调用

ORACLE游标实战:从显式游标到游标变量,一次讲透TaoToken统一Key下的数据库调用 1. ORACLE 游标到底解决什么问题为什么批量数据处理绕不开它刚接触 PL/SQL 的时候我总觉得游标是个多余的东西——明明一条SELECT就能出结果为什么还要OPEN、FETCH、CLOSE折腾一圈后来做批量对账、逐行校验、按条件动态改数的活儿多了才明白游标真正解决的是「结果集太大不能一次性拿进内存」和「需要对每一行做不同处理」这两类问题。普通SELECT INTO只能接一行多行直接抛TOO_MANY_ROWS而游标是一根指针指到哪行处理哪行内存占用可控逻辑也清晰。ORACLE 里的游标分两大类显式游标和隐式游标。显式游标是你自己CURSOR xxx IS SELECT ...声明出来的完全由你控制打开、提取、关闭的节奏隐式游标是 ORACLE 为每条 DML 语句和单行SELECT INTO自动开的你通过SQL%FOUND、SQL%ROWCOUNT这些属性去读它的状态。再往上还有游标 FOR 循环它把OPEN/FETCH/CLOSE三件套自动包好代码量直接砍一半以及 REF CURSOR 游标变量它把「游标」变成可以当参数传递、可以在运行时动态绑定不同查询的变量是做通用存储过程接口的关键。这篇要讲的不只是语法。实际项目里数据库调用往往不是本地sqlplus直连而是通过统一的 API 通道去访问。我这边用的是 TaoToken 的统一 Key 来管理数据库相关的调用凭证好处是多个环境、多个脚本共用一套鉴权不用在每个客户端里散落配置。下面我会把游标的声明、遍历、参数化、更新、REF CURSOR 全部走一遍同时给出通过统一 Key 通道连接数据库的配置示例最后用真实报错带你排一遍坑。适合已经会写基础 SQL、想把手写循环升级成规范游标用法的同学。2. TaoToken 统一 Key 前置准备把数据库调用凭证收拢到一处在写游标代码之前先把「怎么连上数据库」这件事理清楚。很多人的做法是在每个 PL/SQL 脚本、每个客户端工具里各填一份连接串和账号密码环境一多就乱改一次密码要翻十几个地方。TaoToken 的思路是给你一个统一的 Key所有走 API 通道的调用都用它鉴权数据库连接配置集中管理。你需要先拿到这个 Key。打开官网 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 注册后进入控制台在 API Keys 页面创建一个新 Key。控制台地址是 https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewrite 创建时建议按用途命名比如oracle-cursor-dev方便后面区分开发和生产。Key 生成后只显示一次复制下来存到安全的地方。拿到 Key 之后API 入口统一是 https://taotoken.net/api 注意这个地址不带任何查询参数是纯净的 base url。如果你用的是支持自定义 base url 的数据库客户端或 SDK把请求指向它再在请求头里带上Authorization: Bearer 你的Key即可。模型对话相关的调试可以在 https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_contentmodelsutm_campaignrewrite 里先验证通道是否通确认 Key 有效再往下写游标逻辑。这里要强调一点TaoToken 是统一调用通道不是让你绕过数据库本身的权限体系。你的 ORACLE 账号该有的表权限、存储过程执行权限一个都不能少Key 只是把「谁在调用」这件事统一管起来。所以前置准备分两步——先在 ORACLE 侧确认你的账号能查EMP、DEPT这些表再在 TaoToken 侧确认 Key 能正常鉴权。两步都通了后面的游标代码才有意义。如果你打算长期跑批量任务或者做 Agent 化的数据库操作可以了解下 Coding Plan https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite 它更适合持续性的编码和自动化场景比单次调用省心。接入文档在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 遇到参数不明白的先查这里。3. 可复制配置游标声明与统一 Key 连接参数这一节直接上可复制的代码和配置。先给连接层的配置片段再给游标本身的声明与遍历代码。3.1 统一 Key 连接配置JSON 片段假设你用一个支持 HTTP 调用的数据库网关来转发 SQL配置文件taotoken-oracle.json可以这样写{ base_url: https://taotoken.net/api, auth: { type: bearer, api_key: sk-你的TaoTokenKey }, database: { dialect: oracle, host: 127.0.0.1, port: 1521, service_name: ORCLPDB1, user: scott, password: tiger }, options: { fetch_size: 200, auto_commit: false } }注意base_url就是 https://taotoken.net/api 不要在后面拼/v1之类的路径具体端点由客户端决定。fetch_size控制每次从游标取多少行到客户端批量处理时调大能减少往返但别超过内存承受范围。3.2 显式游标 FETCH 循环完整可跑这是最经典的写法OPEN打开LOOP里FETCH用%NOTFOUND判断退出最后CLOSEDECLARE CURSOR c_job IS SELECT empno, ename, job, sal FROM emp WHERE job MANAGER; c_row c_job%ROWTYPE; BEGIN OPEN c_job; LOOP FETCH c_job INTO c_row; EXIT WHEN c_job%NOTFOUND; DBMS_OUTPUT.PUT_LINE(c_row.empno || - || c_row.ename || - || c_row.job || - || c_row.sal); END LOOP; CLOSE c_job; END; /c_job%ROWTYPE让c_row自动匹配游标查询的列结构加列减列不用改声明。EXIT WHEN c_job%NOTFOUND必须放在FETCH之后、处理逻辑之前否则最后一行会被漏掉或者多处理一次空行。3.3 游标 FOR 循环推荐日常使用同样的逻辑用 FOR 循环写出来短很多而且不会忘记CLOSEBEGIN FOR c_row IN (SELECT empno, ename, job, sal FROM emp WHERE job MANAGER) LOOP DBMS_OUTPUT.PUT_LINE(c_row.empno || - || c_row.ename || - || c_row.job || - || c_row.sal); END LOOP; END; /如果游标要复用就先声明再在 FOR 里引用DECLARE CURSOR c_job IS SELECT empno, ename, job, sal FROM emp WHERE job MANAGER; BEGIN FOR c_row IN c_job LOOP DBMS_OUTPUT.PUT_LINE(c_row.ename || 薪水 || c_row.sal); END LOOP; END; /3.4 参数化游标按部门/工种过滤游标可以带参数声明时写形参FOR 循环里传实参DECLARE CURSOR c_dept(p_deptno NUMBER) IS SELECT * FROM emp WHERE deptno p_deptno; BEGIN FOR r_emp IN c_dept(20) LOOP DBMS_OUTPUT.PUT_LINE(员工号 || r_emp.empno || 员工名 || r_emp.ename || 工资 || r_emp.sal); END LOOP; END; /参数默认是IN模式也可以写p_job IN NVARCHAR2显式声明。参数化游标的好处是同一个查询结构能按不同条件反复用不用为每个部门写一条 SQL。3.5 更新游标FOR UPDATE WHERE CURRENT OF要对游标当前行做更新声明时加FOR UPDATE OF 列名更新时用WHERE CURRENT OF 游标名锁定当前行DECLARE CURSOR csr_update IS SELECT * FROM emp1 FOR UPDATE OF sal; emp_info csr_update%ROWTYPE; new_sal emp1.sal%TYPE; BEGIN FOR emp_info IN csr_update LOOP IF emp_info.sal 1500 THEN new_sal : emp_info.sal * 1.2; ELSIF emp_info.sal 2000 THEN new_sal : emp_info.sal * 1.5; ELSIF emp_info.sal 3000 THEN new_sal : emp_info.sal * 2; ELSE new_sal : emp_info.sal; END IF; UPDATE emp1 SET sal new_sal WHERE CURRENT OF csr_update; END LOOP; COMMIT; END; /WHERE CURRENT OF比用主键再查一次快因为它直接定位游标当前指向的物理行。注意FOR UPDATE会加行锁批量更新完记得COMMIT否则锁一直挂着。3.6 REF CURSOR 游标变量动态结果集REF CURSOR 分强类型和弱类型。强类型绑定返回结构弱类型SYS_REFCURSOR什么都能接DECLARE TYPE t_emp_cur IS REF CURSOR RETURN emp%ROWTYPE; v_cur t_emp_cur; v_row emp%ROWTYPE; BEGIN OPEN v_cur FOR SELECT * FROM emp WHERE deptno 10; LOOP FETCH v_cur INTO v_row; EXIT WHEN v_cur%NOTFOUND; DBMS_OUTPUT.PUT_LINE(v_row.ename || - || v_row.sal); END LOOP; CLOSE v_cur; END; /弱类型更灵活适合做通用存储过程出参CREATE OR REPLACE PROCEDURE get_emps_by_job( p_job IN VARCHAR2, p_cur OUT SYS_REFCURSOR ) AS BEGIN OPEN p_cur FOR SELECT empno, ename, sal FROM emp WHERE job p_job; END; /调用时把SYS_REFCURSOR变量传进去过程内部OPEN ... FOR动态绑定查询调用方拿到结果集自己遍历。这是做报表接口、通用查询服务的标准套路。4. 验证请求与成功结果跑通游标并确认输出代码写完必须验证。分两步先在数据库侧确认游标逻辑正确再通过统一 Key 通道确认调用链路通。4.1 数据库侧验证用sqlplus或任意客户端连上 ORACLE先建测试数据CREATE TABLE emp1 AS SELECT * FROM emp; SELECT COUNT(*) FROM emp1;然后执行 3.5 的更新游标块执行前先看一遍原始薪水SELECT empno, ename, sal FROM emp1 ORDER BY empno;执行 PL/SQL 块后DBMS_OUTPUT会逐行打印更新前后的对比。如果客户端不显示输出先执行SET SERVEROUTPUT ON。再查一次表确认薪水按规则变了SELECT empno, ename, sal FROM emp1 ORDER BY empno;预期结果是薪水低于 1500 的涨了 20%1500 到 2000 之间的涨了 50%2000 到 3000 之间的翻倍3000 以上不变。如果某一行没变检查IF/ELSIF的边界条件是不是写反了。4.2 通过统一 Key 通道验证如果你是通过 API 通道发 SQL用curl验证一下鉴权和连通性curl -X POST https://taotoken.net/api/query \ -H Authorization: Bearer sk-你的TaoTokenKey \ -H Content-Type: application/json \ -d { dialect: oracle, sql: SELECT empno, ename, sal FROM emp1 WHERE ROWNUM 5, fetch_size: 5 }返回 200 且 body 里有rows数组说明 Key 有效、通道正常、SQL 能执行。如果返回 401说明 Key 不对或没带Authorization头如果返回连接类错误检查database配置里的 host/port/service_name 是否和你的 ORACLE 实例一致。4.3 游标 FOR 循环的批量验证用一个稍大的数据集验证 FOR 循环的稳定性DECLARE v_count NUMBER : 0; BEGIN FOR r IN (SELECT * FROM emp1 WHERE sal 1000) LOOP v_count : v_count 1; END LOOP; DBMS_OUTPUT.PUT_LINE(符合条件的行数 || v_count); END; /输出行数应该和SELECT COUNT(*) FROM emp1 WHERE sal 1000的结果一致。不一致就说明游标过滤条件或循环退出逻辑有问题。5. 本篇常见错排查401、local proxy failed、reading choices、OAuth游标本身语法错误好查难查的是调用链路上的报错。下面按真实遇到的顺序排一遍。401 Unauthorized。最常见的原因是 Key 没带、带错、或者带了多余空格。检查Authorization: Bearer sk-xxx里Bearer和 Key 之间是一个空格Key 前后没有换行。另一个原因是 Key 被禁用或过期去控制台 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite 确认状态。如果 Key 是对的还报 401检查请求是不是发到了错误的 base url必须是 https://taotoken.net/api 。local proxy failed。这个报错通常出现在客户端配置了本地转发但转发进程没起来或者端口被占用。先确认你的数据库客户端没有开启额外的本地转发设置把连接直接指向统一通道。如果确实需要本地转发检查转发进程的日志确认监听端口和配置文件里写的一致。这个错和游标代码无关是连接层的问题。reading choices 相关报错。这类错误一般出现在解析返回结果时客户端期望的字段名和实际返回的不一致。比如你配置里写了fetch_size但服务端返回的是fetchSize或者返回体里rows字段为空但代码直接取rows[0]。解决办法是先打印完整返回体对照接入文档 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 里的响应结构把字段名对齐。游标返回多行时确保你的解析逻辑是遍历数组而不是只取第一项。OAuth 相关报错。如果你用的是需要 OAuth 流程的客户端报错通常是因为 token 过期或 scope 不足。统一 Key 模式下一般用 Bearer 就行不需要走完整 OAuth。如果客户端强制走 OAuth检查回调地址和 client 配置确认 token 端点指向正确。实在绕不过去换用直接带 Key 的调用方式。游标自身的坑。%NOTFOUND在OPEN之后、第一次FETCH之前是 NULL不是 TRUE所以判断要放在FETCH之后。FOR UPDATE没COMMIT会导致后续会话锁等待。REF CURSOR 作为出参时调用方负责CLOSE过程内部不要关。参数化游标传NULL时WHERE 列 NULL永远不成立要用IS NULL或者NVL处理。6. 把游标用顺之后统一 Key 让数据库调用更省心游标这东西语法就那么多真正拉开差距的是用对场景。单行查询用SELECT INTO加隐式游标属性就够了别硬上显式游标多行遍历优先用 FOR 循环代码短还不容易漏CLOSE需要动态结果集或者做存储过程出参再上 REF CURSOR。更新操作记得FOR UPDATE OF配WHERE CURRENT OF批量完COMMIT。统一 Key 的价值在于把「连哪个库、用什么凭证」这件事从每个脚本里抽出来。你写游标逻辑的时候只管 SQL 和 PL/SQL鉴权和通道交给 TaoToken。调试模型相关的能力可以去 https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_contentmodelsutm_campaignrewrite 先验证通道长期跑批量任务考虑 Coding Plan https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite 。Key 管理在 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite 接入细节查 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 。最后留一个我常用的调试习惯写复杂游标之前先把SELECT单独跑一遍确认结果集和行数符合预期再包进游标。这样出问题的时候能快速定位是 SQL 写错了还是游标逻辑写错了。游标 FOR 循环里如果要做 DML记得把COMMIT放在循环外循环内频繁提交会拖慢速度也容易破坏事务一致性。
返回列表