☰
Oracle PL/SQL 完整教程:从入门到精通(二)——IF、CASE 与游标配 TaoToken 的 settings.json 骨架
2026/9/26 2:43:36 网站建设 项目流程

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_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= API 地址:https://taotoken.net/api

接入前你需要先拿到 Key。打开 API Keys 管理页:https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api_keys&utm_campaign=rewrite ,创建一个新 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,不是ELSEIF:

DECLARE 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、DELETE,Oracle 都会自动创建一个隐式游标。你可以用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游标是否打开可用永远 FALSE

5. 验证请求与成功结果

配置和语法都写完后,跑一个综合示例,把 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 keyword
CASE 表达式结尾是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_source=taotoken_aicg_blog_end&utm_content=model_chat&utm_campaign=rewrite

如果你要长期写存储过程、做 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

下一篇会讲异常处理和动态 SQL,到时候你可以把settings.json里的systemPrompt改成「你是一个 Oracle PL/SQL 异常处理专家」,让 AI 帮你分析RAISE_APPLICATION_ERROR的错误码设计。

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

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

立即咨询