ARTICLE DETAIL

资讯详情

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

Oracle 存储过程实战:按部门号返回员工姓名、工资与佣金(TaoToken 配置骨架)

Oracle 存储过程实战:按部门号返回员工姓名、工资与佣金(TaoToken 配置骨架) 1. 从部门号到员工清单这个存储过程到底解决什么问题你在 Oracle 里维护员工表业务方隔三差五来一句「把 20 号部门的人、工资、佣金拉出来看看」。每次都手写一条select ename, sal, comm from emp where deptno 20当然能跑但问题是调用方可能是个 Java 服务、可能是个报表脚本、也可能是个刚入门的同事他们不想关心表结构只想「给个部门号拿回一列结果」。这时候把查询逻辑封进存储过程就是最省心的做法。这篇要做的是定义一个接收部门号、返回该部门员工姓名、工资、佣金的 PL/SQL 存储过程。我会给你两套可复制的写法一套用显式游标配合dbms_output直接打印适合在 SQL*Plus 或 SQL Developer 里快速验证另一套用sys_refcursor把结果集作为out参数抛出去适合被外部程序消费。两套都会给出建表脚本、过程定义、调用脚本和结果校验动作。同时因为现在很多团队会用统一的模型/API 通道来辅助写 SQL、生成存储过程骨架、做代码审查我也会把 TaoToken 的settings.json配置骨架和连通性验证动作一并给出。目标很明确你照着走一遍查询能跑通结果能对上配置能验证。适合谁看刚接触 PL/SQL 存储过程、被游标和%rowtype绕晕的开发者需要把查询封装成接口给外部调用的后端同学以及想用统一 Key 通道来辅助生成和检查 SQL 的人。2. 前置准备TaoToken 统一 Key 与 API 通道配置骨架在写存储过程之前先把工具链配好。TaoToken 提供统一的 Key 和 API 通道官网入口是 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 基址是 https://taotoken.net/api 。它的作用是让你用一个 Key 去访问多种模型能力写 SQL、生成存储过程、排查报错时不用来回切换账号。先拿到 Key进入控制台 https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 在 API Keys 页面创建一个新 Key复制保存。这个 Key 就是后面settings.json里的凭证。然后在你的项目或工具目录下建一个settings.json配置骨架如下{ provider: taotoken, api_base: https://taotoken.net/api, api_key: sk-你的TaoTokenKey, model: claude-sonnet, timeout: 60, max_tokens: 4096 }几个字段说明一下。api_base固定填https://taotoken.net/api不要带多余路径api_key换成你刚创建的那串model按你实际要用的模型名填timeout给 60 秒生成较长的存储过程时不容易断。注意settings.json里不要提交真实 Key 到 Git 仓库建议用环境变量注入或者把文件加进.gitignore。配置完成后做一次连通性验证。用 curl 发一个最小请求curl -X POST https://taotoken.net/api/v1/messages \ -H Content-Type: application/json \ -H x-api-key: sk-你的TaoTokenKey \ -H anthropic-version: 2023-06-01 \ -d { model: claude-sonnet, max_tokens: 64, messages: [{role: user, content: 回复 ok}] }如果返回里有正常的文本内容说明 Key 和通道都通了。这一步别跳过后面用模型辅助生成存储过程时通道不通会浪费很多时间在排查上。3. 可复制配置建表脚本与两套存储过程定义先把测试数据准备好。假设我们用经典的emp表结构建表和插数据脚本如下-- 建表 create table emp ( empno number(4) primary key, ename varchar2(20), sal number(7,2), comm number(7,2), deptno number(2) ); -- 插入测试数据 insert into emp values (7369, SMITH, 800, null, 20); insert into emp values (7499, ALLEN, 1600, 300, 30); insert into emp values (7521, WARD, 1250, 500, 30); insert into emp values (7566, JONES, 2975, null, 20); insert into emp values (7788, SCOTT, 3000, null, 20); insert into emp values (7839, KING, 5000, null, 10); commit;这样 20 号部门有 SMITH、JONES、SCOTT 三个人佣金都是 null30 号部门有 ALLEN、WARD佣金分别是 300 和 500。后面校验结果时用得上。3.1 方案一显式游标 dbms_output 打印这套写法适合在数据库客户端里直接看输出。核心是用emp.deptno%type让入参类型跟随表字段用mysor%rowtype承接整行数据。create or replace procedure mydure(dno emp.deptno%type) is cursor mysor is select ename, sal, comm from emp where deptno dno; message mysor%rowtype; begin open mysor; loop fetch mysor into message; exit when mysor%notfound; dbms_output.put_line( 员工姓名 || message.ename || 工资 || message.sal || 佣金 || nvl(to_char(message.comm), 无) ); end loop; close mysor; end; /这里有个细节值得说comm是number类型直接和字符串拼接时如果值是 null整个拼接结果会变成 null输出就空了。所以用nvl(to_char(message.comm), 无)兜一下保证佣金为空时也能看到「无」而不是一片空白。调用脚本set serveroutput on; declare d_dno emp.deptno%type : 20; begin mydure(d_dno); end; /set serveroutput on必须加否则dbms_output.put_line的内容不会显示。执行后预期输出三行分别是 SMITH、JONES、SCOTT 的姓名、工资和佣金。3.2 方案二sys_refcursor 作为 out 参数返回结果集如果调用方是 Java、Python 这类外部程序方案一的打印方式就没法用了得把结果集抛出去。sys_refcursor是系统预定义的动态游标正好干这个。create or replace procedure proc_2( pno emp.deptno%type, list_cur out sys_refcursor ) is begin open list_cur for select ename, sal, comm from emp where deptno pno; end; /调用时定义一个sys_refcursor变量再定义一个记录类型来承接每一行set serveroutput on; declare pno emp.deptno%type : 20; mycur sys_refcursor; type emp_rec is record( pname emp.ename%type, psal emp.sal%type, pcomm emp.comm%type ); list_rec emp_rec; begin proc_2(pno, mycur); loop fetch mycur into list_rec; exit when mycur%notfound; dbms_output.put_line( list_rec.pname || || list_rec.psal || || nvl(to_char(list_rec.pcomm), 无) ); end loop; close mycur; end; /方案二的好处是结果集结构清晰外部程序拿到游标后可以按列名取值不用解析字符串。实际项目里我更推荐这套扩展性更好。4. 验证请求与成功结果跑通查询并核对数据配置和过程都写好了现在做完整验证。按顺序执行下面几步。第一步确认过程编译成功。查一下数据字典select object_name, status from user_objects where object_type PROCEDURE and object_name in (MYDURE, PROC_2);status应该是VALID。如果是INVALID多半是表名或字段名写错了用show errors procedure mydure;看具体报错行。第二步跑方案一的调用入参给 20。预期输出员工姓名SMITH 工资800 佣金无 员工姓名JONES 工资2975 佣金无 员工姓名SCOTT 工资3000 佣金无第三步把入参换成 30再跑一次。预期输出员工姓名ALLEN 工资1600 佣金300 员工姓名WARD 工资1250 佣金500第四步跑方案二的调用入参给 20输出应该和方案一一致只是格式变成空格分隔。第五步做一个边界校验传一个不存在的部门号比如 99。两个过程都应该不报错只是没有任何输出行。这说明游标的%notfound退出逻辑是正常的。如果你在验证过程中想用模型帮忙检查 SQL 语法或者生成更多测试用例可以走模型对话入口 https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 把过程定义贴进去让它帮你审一遍。长期做编码和 Agent 任务的可以看 Coding Plan https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 接入文档在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 。5. 本篇常见错排查游标、类型与输出那些坑实际写的时候报错基本集中在几个地方。我按出现频率排一下。ORA-00942 表或视图不存在。最常见的原因是当前登录用户和表所属用户不一致。用select table_name from user_tables where table_name EMP;确认表在当前 schema 下可见。如果表在别的 schema要么加 schema 前缀要么建同义词。ORA-06550 / PLS-00201 标识符必须声明。多半是过程名拼错或者过程没编译成功。先查user_objects的 status再show errors看具体行。dbms_output 没有任何输出。九成是忘了set serveroutput on。在 SQL*Plus 里这是会话级设置每次新开窗口都要重新执行。SQL Developer 里则要确认「DBMS Output」面板已经启用。佣金列输出为空白。前面提过number类型的 null 和字符串拼接会传染。解决办法就是nvl(to_char(comm), 无)先转字符再兜底。sys_refcursor 调用后报 ORA-01000 超出打开游标数。这是游标没关。方案二的调用脚本里close mycur;不能漏外部程序消费完结果集后也要记得关闭。入参类型不匹配。用emp.deptno%type的好处是类型自动跟随但如果你传的是字符串20而不是数字20Oracle 会尝试隐式转换某些情况下会出问题。调用时保证类型一致。提示如果过程体改了但调用还是旧结果先确认create or replace执行成功再检查是不是连到了别的数据库实例。6. 把查询封装好之后接入与后续动作到这里两套存储过程都能跑通结果也核对过了。回到实际工程里方案二更适合作为对外接口因为sys_refcursor能被 JDBC、cx_Oracle、python-oracledb 等直接消费。Java 侧大致是这样调的CallableStatement cs conn.prepareCall({call proc_2(?, ?)}); cs.setInt(1, 20); cs.registerOutParameter(2, OracleTypes.CURSOR); cs.execute(); ResultSet rs (ResultSet) cs.getObject(2); while (rs.next()) { System.out.println(rs.getString(ename) rs.getBigDecimal(sal) rs.getBigDecimal(comm)); }如果你在写这类接入代码时需要生成骨架或者排查类型映射问题用 TaoToken 的 API Keys 页面 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 管理你的 Key接入文档 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 里有各语言的调用示例。Claude Code 相关的接入可以看 https://taotoken.net/claude-code-anthropic?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 。最后留一个实用习惯每次改完存储过程别只看编译状态一定用真实部门号跑一遍调用再传一个不存在的部门号验证空结果分支。这两步花不了一分钟但能挡掉大部分「编译过了、一调就错」的情况。
返回列表