Oracle PL/SQL 完整教程:从入门到精通(二)——IF、CASE 与游标配 TaoToken 的 settings.json 骨架
发布时间:2026/9/26 2:43:40来源:尧图网络
1. 为什么写 PL/SQL 时总在 IF 和游标上卡住如果你刚学完 PL/SQL 的变量声明和DBMS_OUTPUT.PUT_LINE准备动手写第一个存储过程大概率会卡在三个地方IF ... ELSIF ... END IF的收尾分号、CASE两种写法的选择、以及游标OPEN / FETCH / CLOSE三件套到底该写在哪。这些语法本身不难难的是没人告诉你「什么时候用哪种」以及「报错了怎么查」。这篇是 PL/SQL 系列第二篇聚焦条件分支IF、CASE和游标的基础用法目标是让你能独立写出带业务判断的存储过程。同时我会给出一份可直接复制的settings.json骨架把 TaoToken 的统一 Key 和 API 通道接进 AI 编程工具这样你在写 IF/CASE/游标时可以让 AI 帮你补全语法、解释报错、生成测试数据。配置验证通过后再继续往下写代码示例避免边写边怀疑环境。适合谁看已经会DECLARE ... BEGIN ... END;基本结构但写复杂逻辑时容易漏END IF、游标忘记CLOSE、CASE里ELSE写不写拿不准的开发者。下面所有代码都可以直接在 Oracle SQL Developer 或 SQL*Plus 里跑前提是你有employees、departments这两张示例表Oracle 官方 HR schema 自带。2. 前置准备用 TaoToken 统一 Key 接入 AI 编程工具写 PL/SQL 时最烦的是语法细节记不全比如CASE表达式和CASE语句的区别、游标%ROWTYPE怎么声明。这时候让 AI 帮你补全或解释会快很多。TaoToken 的作用是把多个模型的调用统一到一个 Key 和 API 通道上你不需要为每个工具单独配一套凭证。官网入口https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 地址https://taotoken.net/api接入前你需要先拿到 Key。打开 API Keys 管理页https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi_keysutm_campaignrewrite 创建一个新 Key 并复制。这个 Key 后面会写进settings.json的apiKey字段。如果你用的是支持settings.json的 AI 编程工具比如某些 CLI 编码助手配置骨架如下。注意baseUrl用 API 地址不要带 UTM 参数{ provider: taotoken, apiKey: sk-你的TaoToken密钥, baseUrl: https://taotoken.net/api, model: claude-sonnet-4-20250514, maxTokens: 8192, temperature: 0.2, systemPrompt: 你是一个 Oracle PL/SQL 专家回答时给出可直接运行的代码并解释 IF、CASE、游标的语法细节。, timeout: 60000 }几个参数说明temperature设 0.2 是因为写 SQL 需要确定性太高会生成不存在的函数maxTokens给 8192 是为了让 AI 能一次输出完整的存储过程systemPrompt里明确要求「可直接运行」能减少伪代码。配置写完后先别急着写业务逻辑用一条最简单的请求验证通道是否通。你可以用 curl 测curl -X POST https://taotoken.net/api/v1/chat/completions \ -H Content-Type: application/json \ -H Authorization: Bearer sk-你的TaoToken密钥 \ -d { model: claude-sonnet-4-20250514, messages: [ {role: user, content: 用一句话说明 PL/SQL 中 CASE 语句和 CASE 表达式的区别} ] }如果返回里有正常的文本内容说明 Key 和通道都没问题。如果返回 401检查 Key 是否复制完整返回 404检查baseUrl是不是写成了带路径的完整地址。验证通过后再继续下面的语法部分这样遇到报错时你能确定不是环境问题。3. IF 与 CASE条件分支的完整写法3.1 IF 语句的四种形态PL/SQL 的 IF 有四种写法很多人漏分号是因为没记住每种形态的收尾规则。最简单的 IF-THEN只在条件为真时执行DECLARE v_score NUMBER : 85; v_result VARCHAR2(50); BEGIN IF v_score 60 THEN v_result : 及格; END IF; DBMS_OUTPUT.PUT_LINE(结果: || v_result); END; /IF-THEN-ELSE 二选一DECLARE v_score NUMBER : 55; v_grade VARCHAR2(2); BEGIN IF v_score 60 THEN v_grade : P; ELSE v_grade : F; END IF; DBMS_OUTPUT.PUT_LINE(等级: || v_grade); END; /IF-THEN-ELSIF-ELSE 多分支注意ELSIF没有 E不是ELSEIFDECLARE v_score NUMBER : 85; v_grade VARCHAR2(2); v_bonus_rate NUMBER; BEGIN IF v_score 90 THEN v_grade : A; v_bonus_rate : 0.10; ELSIF v_score 80 THEN v_grade : B; v_bonus_rate : 0.05; ELSIF v_score 70 THEN v_grade : C; v_bonus_rate : 0.02; ELSE v_grade : D; v_bonus_rate : 0; END IF; DBMS_OUTPUT.PUT_LINE(等级: || v_grade || , 奖励比例: || v_bonus_rate * 100 || %); END; /嵌套 IF 用于需要二次判断的场景DECLARE v_score NUMBER : 85; v_result VARCHAR2(50) : 及格; BEGIN IF v_score 60 THEN IF v_score 80 THEN v_result : v_result || 优秀; ELSE v_result : v_result || 良好; END IF; END IF; DBMS_OUTPUT.PUT_LINE(结果: || v_result); END; /踩过的坑END IF;后面必须跟分号嵌套时每个IF对应一个END IF;少一个就报ORA-06550。建议写的时候先把IF ... END IF;骨架搭好再往里填内容。3.2 CASE 语句与 CASE 表达式的区别这是最容易混淆的点。CASE 语句是「执行动作」CASE 表达式是「返回一个值」。前者用在BEGIN ... END里做分支处理后者可以直接赋值给变量或用在 SQL 里。简单 CASE 语句匹配固定值DECLARE v_dept_id NUMBER : 50; v_dept_name VARCHAR2(30); BEGIN CASE v_dept_id WHEN 10 THEN v_dept_name : 行政管理; WHEN 20 THEN v_dept_name : 市场营销; WHEN 30 THEN v_dept_name : 采购; WHEN 50 THEN v_dept_name : 运输; ELSE v_dept_name : 其他部门; END CASE; DBMS_OUTPUT.PUT_LINE(部门: || v_dept_name); END; /搜索式 CASE 语句用条件表达式匹配DECLARE v_salary NUMBER : 7500; v_grade VARCHAR2(10); v_bonus NUMBER; BEGIN CASE WHEN v_salary 10000 THEN v_grade : 高级; v_bonus : v_salary * 0.15; WHEN v_salary 6000 THEN v_grade : 中级; v_bonus : v_salary * 0.10; WHEN v_salary 3000 THEN v_grade : 初级; v_bonus : v_salary * 0.05; ELSE v_grade : 实习; v_bonus : v_salary * 0.02; END CASE; DBMS_OUTPUT.PUT_LINE(等级: || v_grade || , 奖金: || v_bonus); END; /CASE 表达式直接返回值DECLARE v_salary NUMBER : 12000; v_level VARCHAR2(20); BEGIN v_level : CASE WHEN v_salary 20000 THEN 非常高 WHEN v_salary 15000 THEN 高 WHEN v_salary 10000 THEN 中等 WHEN v_salary 5000 THEN 一般 ELSE 较低 END; DBMS_OUTPUT.PUT_LINE(薪资水平: || v_level); END; /对比一下CASE 语句里每个分支可以写多条语句用;分隔最后END CASE;CASE 表达式里每个分支只能是一个值最后END没有 CASE可以直接赋值。如果你在SELECT里用只能用 CASE 表达式。4. 游标从隐式到显式再到 FOR 循环4.1 隐式游标与 SQL%ROWCOUNT每次执行SELECT INTO、INSERT、UPDATE、DELETEOracle 都会自动创建一个隐式游标。你可以用SQL%ROWCOUNT、SQL%FOUND、SQL%NOTFOUND查看结果BEGIN UPDATE employees SET salary salary * 1.05 WHERE department_id 50; DBMS_OUTPUT.PUT_LINE(更新行数: || SQL%ROWCOUNT); IF SQL%FOUND THEN DBMS_OUTPUT.PUT_LINE(更新成功); END IF; ROLLBACK; END; /注意SQL%ISOPEN对隐式游标永远是 FALSE因为 Oracle 自动开关。4.2 显式游标的四步走显式游标需要你手动声明、打开、提取、关闭DECLARE CURSOR c_emp IS SELECT employee_id, first_name, last_name, salary FROM employees WHERE department_id 50 ORDER BY salary DESC; v_emp c_emp%ROWTYPE; v_count NUMBER : 0; BEGIN OPEN c_emp; LOOP FETCH c_emp INTO v_emp; EXIT WHEN c_emp%NOTFOUND; v_count : v_count 1; DBMS_OUTPUT.PUT_LINE(v_count || . || v_emp.first_name || || v_emp.last_name || - || v_emp.salary); END LOOP; CLOSE c_emp; DBMS_OUTPUT.PUT_LINE(总人数: || v_count); END; /关键点EXIT WHEN c_emp%NOTFOUND;必须放在FETCH之后、处理逻辑之前否则最后一行会被处理两次或漏掉。CLOSE不能忘否则游标一直占资源。4.3 带参数的游标参数化游标让同一个游标适配不同查询条件DECLARE CURSOR c_dept_emp (p_dept_id NUMBER, p_min_sal NUMBER DEFAULT 0) IS SELECT first_name, last_name, salary FROM employees WHERE department_id p_dept_id AND salary p_min_sal ORDER BY salary DESC; v_emp c_dept_emp%ROWTYPE; BEGIN OPEN c_dept_emp(50, 5000); LOOP FETCH c_dept_emp INTO v_emp; EXIT WHEN c_dept_emp%NOTFOUND; DBMS_OUTPUT.PUT_LINE(v_emp.first_name || || v_emp.last_name || - || v_emp.salary); END LOOP; CLOSE c_dept_emp; OPEN c_dept_emp(60, 6000); LOOP FETCH c_dept_emp INTO v_emp; EXIT WHEN c_dept_emp%NOTFOUND; DBMS_OUTPUT.PUT_LINE(v_emp.first_name || || v_emp.last_name || - || v_emp.salary); END LOOP; CLOSE c_dept_emp; END; /4.4 游标 FOR 循环最推荐的写法游标 FOR 循环自动处理OPEN / FETCH / CLOSE代码最短最不容易出错BEGIN FOR emp_rec IN ( SELECT employee_id, first_name, last_name, salary FROM employees WHERE department_id 50 ORDER BY salary DESC ) LOOP DBMS_OUTPUT.PUT_LINE(emp_rec.first_name || || emp_rec.last_name || - || emp_rec.salary); END LOOP; END; /如果你已经声明了游标也可以直接FOR rec IN cursor_name LOOP。循环变量emp_rec自动是%ROWTYPE不需要你声明。4.5 游标属性速查属性含义显式游标隐式游标%FOUND最近一次 FETCH 是否取到行可用SQL%FOUND%NOTFOUND最近一次 FETCH 是否没取到行可用SQL%NOTFOUND%ROWCOUNT已提取的行数可用SQL%ROWCOUNT%ISOPEN游标是否打开可用永远 FALSE5. 验证请求与成功结果配置和语法都写完后跑一个综合示例把 IF、CASE、游标串起来。下面这个块会遍历部门 50 的员工根据薪资用 CASE 表达式分级用 IF 判断是否发奖金DECLARE CURSOR c_emp IS SELECT employee_id, first_name, last_name, salary FROM employees WHERE department_id 50 ORDER BY salary DESC; v_level VARCHAR2(20); v_bonus NUMBER; v_total NUMBER : 0; BEGIN FOR emp_rec IN c_emp LOOP v_level : CASE WHEN emp_rec.salary 10000 THEN 高 WHEN emp_rec.salary 5000 THEN 中 ELSE 低 END; IF v_level 高 THEN v_bonus : emp_rec.salary * 0.10; ELSIF v_level 中 THEN v_bonus : emp_rec.salary * 0.05; ELSE v_bonus : 0; END IF; v_total : v_total v_bonus; DBMS_OUTPUT.PUT_LINE( emp_rec.first_name || || emp_rec.last_name || | 薪资: || emp_rec.salary || | 等级: || v_level || | 奖金: || ROUND(v_bonus, 2) ); END LOOP; DBMS_OUTPUT.PUT_LINE(奖金总额: || ROUND(v_total, 2)); END; /成功输出类似Steven King | 薪资: 24000 | 等级: 高 | 奖金: 2400 Neena Kochhar | 薪资: 17000 | 等级: 高 | 奖金: 1700 ... 奖金总额: 12345.67如果DBMS_OUTPUT没显示先执行SET SERVEROUTPUT ON;SQL*Plus或在 SQL Developer 里勾选「DBMS Output」面板的加号。6. 本篇常见报错排查ORA-06550: line X, column Y: PLS-00103: Encountered the symbol END最常见的原因是IF少了END IF;或CASE少了END CASE;。检查每个IF是否配对ELSIF是否写成了ELSEIF。ORA-01001: invalid cursor游标已经CLOSE了还在FETCH或者没OPEN就FETCH。显式游标必须严格按OPEN → FETCH → CLOSE顺序。ORA-06511: cursor already open同一个游标OPEN了两次没CLOSE。如果要在循环里重复用每次OPEN前确保上一次已CLOSE或者直接用游标 FOR 循环。CASE 表达式报 ORA-00905: missing keywordCASE 表达式结尾是END不是END CASE。CASE 语句结尾才是END CASE;。两者别混。SQL%ROWCOUNT 返回 0 但明明更新了数据检查是不是在UPDATE之后又执行了别的 SQL 语句SQL%ROWCOUNT只反映最近一次 DML。要立即读取。游标 FOR 循环里修改了游标查询的表游标 FOR 循环默认是只读的如果你在循环里UPDATE了正在遍历的表可能报ORA-01555或数据不一致。建议先把数据FETCH到集合再批量处理。7. 继续深入把 AI 接入你的 PL/SQL 工作流条件分支和游标是 PL/SQL 存储过程的骨架后面还有异常处理、动态 SQL、包PACKAGE等内容。写复杂逻辑时让 AI 帮你检查语法、生成测试用例会省很多时间。如果你主要用 AI 做模型对话和代码解释可以直接在模型对话页测试https://taotoken.net/model-chat?utm_sourcetaotoken_aicg_blog_endutm_contentmodel_chatutm_campaignrewrite如果你要长期写存储过程、做 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下一篇会讲异常处理和动态 SQL到时候你可以把settings.json里的systemPrompt改成「你是一个 Oracle PL/SQL 异常处理专家」让 AI 帮你分析RAISE_APPLICATION_ERROR的错误码设计。
网站建设高端定制企业官网