1. 项目概述为什么要在SQL里写“如果...那么...”在数据库开发里尤其是处理Oracle这样的企业级数据库我们经常遇到一个场景写一段SQL需要根据某个条件来决定返回什么值或者执行哪段逻辑。比如计算员工奖金时如果销售额超过100万奖金比例是15%否则是10%。又或者在生成报表时需要根据状态码将数字转换成更易读的文本描述。这时候你脑子里蹦出来的第一个念头可能就是编程语言里的if-else语句。但SQL特别是查询语句本质上是声明式的它告诉数据库“我要什么”而不是“一步一步怎么去拿”。所以直接把if (condition) { ... } else { ... }这种命令式语法搬过来是行不通的。但这不代表我们实现不了条件逻辑。相反Oracle提供了好几种非常强大且优雅的方式来实现类似if/else的功能每种都有其最适合的场景和“脾气”。掌握这些写法不仅仅是多会几个语法糖它直接关系到你写出的SQL是清晰高效、易于维护还是冗长晦涩、性能堪忧。今天我就结合十多年踩坑填坑的经验把这三种最核心的写法——CASE表达式、DECODE函数以及在PL/SQL中使用的IF语句——给你掰开揉碎了讲清楚。我们会从最简单的场景入手一直深入到复杂的嵌套和性能对比让你下次再遇到条件判断时能毫不犹豫地选出最趁手的那把“武器”。2. 核心思路与方案选型三种武器的定位与抉择在动手写代码之前我们先得弄明白Oracle提供的这三种实现条件逻辑的方式各自的设计哲学和适用边界是什么。选错了工具事倍功半不说还可能埋下坑。2.1 CASE 表达式功能全面的“瑞士军刀”CASE表达式是SQL标准的一部分也是我个人最推荐、使用频率最高的方式。你可以把它理解为一个更强大、更灵活的if-else if-else链。它有两种形式简单 CASE 表达式类似于switch-case它将一个表达式与一系列值进行比较。搜索 CASE 表达式这才是真正的if-else if-else每个WHEN后面都可以是一个独立的布尔条件。它的核心优势在于标准化和表达能力强。几乎所有的条件逻辑都能用CASE清晰表达而且因为它属于SQL标准可移植性好代码意图一目了然。在纯SQL查询SELECT, WHERE, ORDER BY, UPDATE的SET子句等中实现条件逻辑CASE是首选。2.2 DECODE 函数Oracle特色的“快捷键”DECODE是Oracle独有的一个函数历史非常悠久。它的语法看起来有点像CASE的简化版但底层实现和思维模式有所不同。DECODE更像是“值替换”函数DECODE(expr, search1, result1, search2, result2, ..., default)。它的优点是语法简洁对于简单的等值比较场景写起来比CASE更紧凑。但缺点也很明显首先它非SQL标准代码移植到其他数据库如MySQL, PostgreSQL需要重写其次它只能进行等值比较无法实现、、BETWEEN、LIKE等复杂条件判断最后当参数很多时它的可读性会急剧下降。所以DECODE可以看作是一个在Oracle环境内用于简单等值条件判断的便捷工具但不适合处理复杂逻辑。2.3 PL/SQL IF 语句过程化处理的“重型机械”前两者都是在单条SQL语句内部实现条件判断。而IF语句则属于PL/SQLOracle的过程化语言扩展的范畴。当你的业务逻辑异常复杂需要多条SQL语句、循环、异常处理等过程化控制时就必须在PL/SQL块存储过程、函数、触发器等中使用IF语句。IF语句提供了完整的命令式编程能力包括IF-THEN、IF-THEN-ELSE、IF-THEN-ELSIF以及嵌套。它用于控制程序的执行流程而不仅仅是计算一个值。简单总结一下选型思路在单条SQL查询中做条件判断并返回值优先用CASE表达式。在Oracle中做简单的等值替换且确定不涉及数据库迁移可以考虑用DECODE函数图个书写方便。要编写包含复杂业务逻辑、事务控制、多步操作的存储过程或脚本必须使用PL/SQL 中的IF语句。注意很多新手会混淆CASE表达式和IF语句的使用场景。记住一个关键点CASE产生一个“值”用于SELECT列表或赋值而IF决定执行哪一段“代码”。在PL/SQL中你甚至可以混合使用比如在赋值语句的右边使用CASE表达式。3. 核心细节解析与实操要点接下来我们深入到每种写法的语法细节、使用技巧和那些容易踩坑的地方。3.1 CASE 表达式的两种形态与实战技巧1. 简单 CASE 表达式语法CASE expr WHEN comparison_expr1 THEN result1 [WHEN comparison_expr2 THEN result2 ...] [ELSE default_result] END它先计算CASE后面的表达式expr然后按顺序与每个WHEN后的comparison_exprN进行等值比较。如果匹配则返回对应的THEN值。实战示例将部门编号转换为名称SELECT employee_id, first_name, department_id, CASE department_id WHEN 10 THEN 财务部 WHEN 20 THEN 研发部 WHEN 30 THEN 销售部 ELSE 其他部门 END AS department_name FROM employees;这个例子中CASE对department_id的值进行匹配实现了一个简单的查找映射。2. 搜索 CASE 表达式语法CASE WHEN condition1 THEN result1 [WHEN condition2 THEN result2 ...] [ELSE default_result] END这是功能最全的形式。每个WHEN后面都是一个可以返回布尔值TRUE/FALSE的完整条件表达式。数据库会按顺序评估这些条件一旦某个WHEN条件为真就返回其对应的THEN值。实战示例根据薪水区间计算奖金等级SELECT employee_id, first_name, salary, CASE WHEN salary 15000 THEN A级 WHEN salary BETWEEN 10000 AND 15000 THEN B级 WHEN salary BETWEEN 5000 AND 9999 THEN C级 ELSE D级 END AS bonus_grade FROM employees ORDER BY salary DESC;这里使用了和BETWEEN进行范围判断简单CASE无法实现。核心技巧与避坑指南ELSE子句是可选的但强烈建议总是写上。如果没有ELSE且所有WHEN条件都不满足CASE表达式将返回NULL。这可能导致意想不到的结果。显式地写上ELSE 未知或ELSE 0是防御性编程的好习惯。CASE表达式是有数据类型的。所有THEN子句返回的值以及ELSE子句的值必须是相同的数据类型或可以隐式转换的类型。如果THEN返回数字ELSE返回字符串可能会报错。CASE表达式几乎可以用在SQL的任何地方SELECT列表、WHERE子句、ORDER BY子句、GROUP BY子句、UPDATE的SET部分甚至JOIN的条件里。这是一个非常强大的特性。在WHERE中使用可以动态构造过滤条件但要注意可能对索引使用有影响。-- 根据输入参数动态过滤 SELECT * FROM orders WHERE order_status CASE :input_status WHEN ALL THEN order_status -- 当输入为ALL时条件恒等相当于不过滤 ELSE :input_status END;在ORDER BY中使用实现自定义排序规则。-- 让特定部门如部门20的员工排在前面其他人按姓名排序 SELECT * FROM employees ORDER BY CASE WHEN department_id 20 THEN 1 ELSE 2 END, first_name;性能考虑CASE表达式是按顺序评估的。把最可能为真的条件或者计算成本最低的条件放在前面可以提高效率。对于等值判断数据库优化器可能将其转换为哈希查找效率很高。3.2 DECODE 函数的妙用与局限DECODE的语法可以理解为DECODE(表达式, 搜索值1, 结果1, 搜索值2, 结果2, ..., 默认值)。它依次将“表达式”与每个“搜索值”进行比较。如果相等则返回对应的“结果”。如果所有搜索值都不匹配则返回“默认值”。如果连默认值也没提供就返回NULL。实战示例实现与简单CASE相同的功能SELECT employee_id, first_name, department_id, DECODE(department_id, 10, 财务部, 20, 研发部, 30, 销售部, 其他部门) AS department_name FROM employees;可以看到在这个等值映射的场景下DECODE的写法确实更紧凑一些。高级技巧与严重局限实现简单的“IF-THEN-ELSE”DECODE最常见的用法就是模拟if (ab) then x else y。-- 如果 salary 10000显示‘高薪’否则显示‘一般’ SELECT employee_id, DECODE(SIGN(salary - 10000), -1, 一般, 高薪) AS salary_level FROM employees;这里用SIGN函数判断salary-10000的符号SIGN返回 -1, 0, 1。DECODE判断结果为 -1即小于10000时返回‘一般’否则0或1返回‘高薪’。但这已经显得有点绕了用CASE WHEN salary 10000 THEN ...会更清晰。局限1仅支持等值比较。这是DECODE最大的硬伤。你不能写DECODE(salary, 10000, ...)。所有比较都必须是。局限2可读性差。当参数对超过3组时代码会变得难以阅读和维护。DECODE(a,b1,c1,b2,c2,b3,c3,b4,c4,b5,c5, default)一眼看去很难快速理清逻辑。局限3非标准。这意味着你的SQL代码被绑定在了Oracle上。如果未来有数据库迁移的需求这部分代码必须重写。实操心得在现代Oracle开发中除非是维护非常古老的代码或者你非常确定就是简单的等值映射且追求极简书写否则我建议统一使用CASE表达式。CASE的表达能力、可读性和可维护性全面优于DECODE而且符合SQL标准。把DECODE当作一个知道但少用的备选方案就好。3.3 PL/SQL IF 语句过程化逻辑的核心当逻辑跳出单条SQL需要过程化控制时就必须进入PL/SQL的领域。IF语句在这里是绝对的主角。基本语法结构IF condition THEN statements; [ELSIF condition THEN statements;] [ELSE statements;] END IF;实战示例在存储过程中实现复杂业务逻辑假设我们要编写一个调整员工薪水的存储过程规则很复杂如果员工在‘研发部’且工龄超过5年加薪15%。否则如果在‘销售部’且去年业绩超过100万加薪20%。否则所有其他员工加薪5%。CREATE OR REPLACE PROCEDURE adjust_salary (p_emp_id IN NUMBER) IS v_dept_name employees.department_id%TYPE; v_hire_date employees.hire_date%TYPE; v_sales_amount NUMBER : 0; -- 假设能从其他表获取 v_raise_rate NUMBER; BEGIN -- 首先获取员工的基本信息 SELECT department_id, hire_date INTO v_dept_name, v_hire_date FROM employees WHERE employee_id p_emp_id; -- 这里简化处理实际中v_sales_amount应从销售记录表汇总计算 -- SELECT NVL(SUM(amount), 0) INTO v_sales_amount FROM sales WHERE emp_id p_emp_id AND sale_year EXTRACT(YEAR FROM SYSDATE)-1; -- 使用IF-ELSIF-ELSE实现复杂的条件判断 IF v_dept_name 20 AND MONTHS_BETWEEN(SYSDATE, v_hire_date) / 12 5 THEN v_raise_rate : 0.15; DBMS_OUTPUT.PUT_LINE(研发部老员工加薪15%); ELSIF v_dept_name 30 AND v_sales_amount 1000000 THEN v_raise_rate : 0.20; DBMS_OUTPUT.PUT_LINE(销售部业绩标兵加薪20%); ELSE v_raise_rate : 0.05; DBMS_OUTPUT.PUT_LINE(普通加薪5%); END IF; -- 执行更新操作 UPDATE employees SET salary salary * (1 v_raise_rate) WHERE employee_id p_emp_id; COMMIT; DBMS_OUTPUT.PUT_LINE(员工 || p_emp_id || 薪水调整完毕。); EXCEPTION WHEN NO_DATA_FOUND THEN DBMS_OUTPUT.PUT_LINE(未找到员工ID: || p_emp_id); WHEN OTHERS THEN ROLLBACK; DBMS_OUTPUT.PUT_LINE(调整薪水时发生错误: || SQLERRM); END adjust_salary; /关键要点与最佳实践ELSIF的拼写注意是ELSIF不是ELSEIF也不是ELSE IF。这是PL/SQL特有的关键字新手常拼错。每个IF都必须有对应的END IF忘记END IF是编译错误的常见原因。条件中的布尔值PL/SQL中条件表达式必须得到一个布尔值TRUE/FALSE/NULL。要小心处理NULL因为NULL参与逻辑比较如,结果通常是NULL即未知这会导致整个条件不被认为是TRUE。对于可能为NULL的变量使用IS NULL或IS NOT NULL判断或者用NVL函数赋予默认值。-- 危险的写法如果v_sales_amount为NULL则条件不为TRUE IF v_sales_amount 1000000 THEN ... -- 安全的写法 IF NVL(v_sales_amount, 0) 1000000 THEN ...在PL/SQL中混合使用CASE和IFCASE表达式在PL/SQL中同样可以使用通常用于基于一个变量赋值。-- 在PL/SQL中使用CASE表达式赋值 v_raise_rate : CASE WHEN v_dept_name 20 AND v_years 5 THEN 0.15 WHEN v_dept_name 30 AND v_sales 1000000 THEN 0.20 ELSE 0.05 END;这种写法有时比一长串IF-ELSIF更清晰特别是当所有分支都是为了给同一个变量赋值时。4. 性能对比与高级应用场景了解了基本用法我们还得关心一下它们的性能表现以及在更复杂场景下的应用。4.1 性能考量CASE vs DECODE在大多数情况下CASE表达式和DECODE函数的性能差异是微乎其微的Oracle的优化器会对它们进行高效的内部转换。但在某些边缘场景下选择会影响执行计划。简单等值比较对于直接的等值映射DECODE和简单CASE表达式的性能通常一样好。优化器可能将它们视为相同的操作。复杂条件判断当涉及范围判断、多条件组合AND/OR或函数调用时搜索CASE表达式是唯一的选择DECODE无法实现。此时性能取决于WHEN子句中条件的复杂度和索引的利用情况。可读性与维护性对性能的间接影响复杂的、嵌套的DECODE函数会严重降低代码可读性使得后续优化和问题排查变得困难。而清晰的CASE表达式更容易让优化器理解也更容易被DBA或后来的开发者进行性能调优。从工程角度看可维护性的提升本身就是一种重要的“性能”收益。一个常见的性能陷阱是在WHERE子句中对索引列使用CASE或DECODE这可能导致索引失效进行全表扫描。-- 假设 department_id 上有索引 -- 写法A可能使索引失效 SELECT * FROM employees WHERE DECODE(department_id, 10, A, 20, B, C) A; -- 写法B优化器更可能使用索引 SELECT * FROM employees WHERE department_id 10; -- 使用CASE时同样需要注意 SELECT * FROM employees WHERE (CASE WHEN salary 10000 THEN 1 ELSE 0 END) 1; -- 更好的写法通常是 SELECT * FROM employees WHERE salary 10000;原则是尽量让条件保持为索引列与常量的直接比较避免在索引列上包裹函数或表达式。4.2 嵌套与组合实现复杂业务逻辑现实业务中条件判断往往不是一层那么简单。1. 嵌套 CASE 表达式可以在THEN或ELSE子句中再嵌入一个完整的CASE表达式。SELECT employee_id, salary, CASE WHEN department_id 30 THEN -- 销售部 CASE WHEN salary 5000 THEN 销售新人 WHEN salary BETWEEN 5000 AND 15000 THEN 销售骨干 ELSE 销售精英 END ELSE -- 非销售部 CASE WHEN salary 8000 THEN 普通职员 WHEN salary BETWEEN 8000 AND 20000 THEN 核心职员 ELSE 高级专家 END END AS employee_level FROM employees;虽然嵌套可以实现复杂逻辑但超过两层的嵌套会严重影响可读性。这时就需要考虑是否应该将部分逻辑移到应用层或者使用PL/SQL封装。2. CASE 与聚合函数的结合这是CASE表达式非常强大的一个应用场景常用于条件计数和条件求和。-- 统计每个部门不同薪资等级的人数 SELECT department_id, COUNT(*) AS total_emp, COUNT(CASE WHEN salary 5000 THEN 1 END) AS low_salary_cnt, -- 注意这里 ELSE 默认为 NULLCOUNT忽略NULL COUNT(CASE WHEN salary BETWEEN 5000 AND 10000 THEN 1 END) AS mid_salary_cnt, SUM(CASE WHEN department_id 30 THEN salary ELSE 0 END) AS total_sales_salary, -- 条件求和只计算销售部的薪水总额 AVG(CASE WHEN commission_pct IS NOT NULL THEN salary END) AS avg_salary_with_comm -- 条件平均值只计算有佣金员工的平均薪资 FROM employees GROUP BY department_id ORDER BY department_id;这种“条件聚合”避免了多次扫描表或使用子查询能极大提升复杂统计查询的性能。4.3 在UPDATE和MERGE语句中的应用CASE表达式在数据更新操作中同样大放异彩。动态UPDATE根据不同条件更新为不同的值UPDATE employees e SET e.salary CASE WHEN e.department_id 20 THEN e.salary * 1.10 -- 研发部加薪10% WHEN e.department_id 30 AND e.hire_date DATE 2020-01-01 THEN e.salary * 1.15 -- 销售部老员工加薪15% ELSE e.salary * 1.05 -- 其他人加薪5% END, e.comment 于 || TO_CHAR(SYSDATE, YYYY-MM-DD) || 进行普调。 WHERE e.hire_date DATE 2023-01-01; -- 只调整2023年以前入职的员工一条UPDATE语句通过CASE实现了多套更新规则代码简洁且原子性有保证。在MERGE语句中MERGEUPSERT语句的WHEN MATCHED THEN UPDATE子句中也可以使用CASE来条件化地更新目标表。MERGE INTO employee_bonus dest USING (SELECT employee_id, salary, department_id FROM employees WHERE hire_date DATE 2022-01-01) src ON (dest.emp_id src.employee_id) WHEN MATCHED THEN UPDATE SET dest.bonus CASE WHEN src.salary 12000 THEN src.salary * 0.20 WHEN src.department_id 30 THEN src.salary * 0.25 -- 销售部奖金系数高 ELSE src.salary * 0.15 END WHEN NOT MATCHED THEN INSERT (emp_id, bonus) VALUES (src.employee_id, src.salary * 0.10); -- 新员工默认奖金5. 常见问题与排查技巧实录在实际开发中使用这些条件判断功能时总会遇到一些“坑”。下面是我总结的几个典型问题和解决方法。5.1 “ORA-00932: 数据类型不一致”错误这是使用CASE和DECODE时最常见的错误之一。问题场景SELECT employee_id, CASE WHEN salary 10000 THEN High WHEN salary 5000 THEN 5000 -- 这里返回数字 ELSE Low -- 这里返回字符串 END AS category FROM employees;上面的SQL会报错因为THEN子句返回了字符串High和Low但第二个WHEN返回了数字5000。Oracle无法确定这个CASE表达式最终的数据类型。解决方案 确保所有THEN和ELSE分支返回的数据类型兼容。如果需要混合类型可以使用TO_CHAR、TO_NUMBER等函数进行显式转换。-- 正确写法统一为字符串 SELECT employee_id, CASE WHEN salary 10000 THEN High WHEN salary 5000 THEN 5000 -- 将数字转为字符串 ELSE Low END AS category FROM employees; -- 或者统一为数字如果逻辑允许 SELECT employee_id, CASE WHEN salary 10000 THEN 1 -- 用数字代码表示 WHEN salary 5000 THEN 2 ELSE 3 END AS category_code FROM employees;对于DECODE函数同样要遵循这个规则。5.2 处理NULL值的陷阱NULL在条件判断中是个特殊存在它不等于任何值甚至不等于它自己。问题1用判断NULLDECLARE v_commission employees.commission_pct%TYPE; BEGIN SELECT commission_pct INTO v_commission FROM employees WHERE employee_id 100; IF v_commission NULL THEN -- 这个条件永远为 FALSE DBMS_OUTPUT.PUT_LINE(No commission); END IF; END; /上面的代码块什么都不会输出因为v_commission NULL的结果是NULL未知在IF语句中NULL不被视为TRUE。正确做法使用IS NULL或IS NOT NULL。IF v_commission IS NULL THEN DBMS_OUTPUT.PUT_LINE(No commission); END IF;问题2CASE/WHEN 中的NULL在CASE表达式中WHEN NULL THEN ...这个分支永远不会被执行因为WHEN后面的条件NULL不是TRUE。SELECT CASE NULL WHEN NULL THEN Is Null ELSE Not Null END FROM dual; -- 返回 Not Null如果想判断某个字段是否为NULL必须在WHEN后面使用IS NULL。SELECT CASE WHEN commission_pct IS NULL THEN 无佣金 ELSE 佣金比例 || TO_CHAR(commission_pct) END AS comm_info FROM employees;5.3 条件顺序错误导致逻辑BUGCASE表达式和IF-ELSIF语句都是顺序执行的第一个满足的条件会“拦截”后面的判断。典型错误示例SELECT salary, CASE WHEN salary 0 THEN 有薪水 WHEN salary 5000 THEN 高薪水 -- 这行永远执行不到 ELSE 无薪水 END AS salary_desc FROM employees;对于薪水为10000的员工第一个条件salary 0已经为真所以返回有薪水永远不会走到判断salary 5000的分支。正确写法必须把更严格、更具体的条件放在前面。SELECT salary, CASE WHEN salary 5000 THEN 高薪水 WHEN salary 0 THEN 有薪水 ELSE 无薪水 END AS salary_desc FROM employees;5.4 DECODE函数参数个数为奇数时的诡异行为DECODE的参数应该是expr, search1, result1, search2, result2, ..., default。即总参数个数应为奇数expr 若干对search/result 一个default。如果你不小心传了偶数个参数比如漏掉了最后的默认值会发生什么SELECT DECODE(1, 1, One, 2, Two) FROM dual;你可能会期望当expr(1)不匹配search1(1)时去匹配search2(2)发现也不匹配然后因为没提供默认值而返回NULL。但实际结果是返回One。因为Oracle把最后一个参数Two当成了search2的result而2被当成了search2。整个函数被解释为DECODE(1, 1, One, 2, Two)其中1匹配了第一个搜索值1所以返回One。更诡异的是SELECT DECODE(3, 1, One, 2, Two) FROM dual;这里expr(3)不匹配search1(1)也不匹配search2(2)。按照奇数参数的逻辑Two是result2没有默认值应该返回NULL。但实际上Oracle会返回NULL。看起来似乎合理但考虑这个SELECT DECODE(3, 1, One, 2, Two, Three) FROM dual; -- 正确返回 Three SELECT DECODE(3, 1, One, 2, Two, Three, Four) FROM dual; -- 返回什么最后这个例子参数个数是偶数6个。Oracle会将其解析为DECODE(3, 1, One, 2, Two, Three, Four)。即搜索值1对应结果One搜索值2对应结果Two搜索值Three对应结果Four。expr(3)与字符串Three比较不相等又没有更多参数作为默认值所以返回NULL。结论与建议DECODE函数对参数个数的处理规则比较隐晦容易出错。强烈建议在写DECODE时总是显式地提供最后一个参数作为默认值即使你希望默认返回NULL也最好写成DECODE(..., NULL)这样意图最清晰。更好的建议是尽量用CASE表达式替代。5.5 性能排查为什么这个带CASE的查询这么慢当发现一个包含复杂CASE或DECODE的查询性能不佳时可以按以下步骤排查查看执行计划使用EXPLAIN PLAN FOR ...或 SQL Developer 等工具的“解释计划”功能。重点关注全表扫描TABLE ACCESS FULL是否因为CASE中的条件导致无法使用索引例如WHERE CASE col WHEN ...。昂贵的函数计算CASE的WHEN或THEN子句中是否包含了复杂的函数如TO_CHAR(date_col, ...)、SUBSTR、自定义函数这些计算可能对每一行都执行消耗巨大。考虑将计算移到结果集有时在SELECT列表中使用CASE进行计算是合适的。但如果这个计算值又作为过滤条件出现在WHERE子句中就会导致问题。例如-- 慢WHERE子句中的CASE导致每一行都要先计算category SELECT * FROM ( SELECT e.*, CASE WHEN e.salary 10000 THEN HIGH ELSE LOW END as category FROM employees e ) WHERE category HIGH; -- 快将条件直接写在WHERE子句中 SELECT e.*, HIGH as category -- 或者用CASE在SELECT中显示 FROM employees e WHERE e.salary 10000;使用物化视图或函数索引对于极其复杂且频繁使用的CASE逻辑如果查询性能是关键可以考虑创建基于该CASE表达式的函数索引如果过滤条件依赖它或者将结果物化到另一个表或物化视图中。-- 创建函数索引示例需谨慎评估维护成本 CREATE INDEX idx_emp_salary_level ON employees ( CASE WHEN salary 10000 THEN HIGH WHEN salary 5000 THEN MID ELSE LOW END );简化逻辑检查CASE表达式是否可以简化。多层嵌套的CASE不仅难读也可能难优化。有时将其拆分成多个简单的CASE或者将部分逻辑用UNION ALL分开查询性能反而更好。踩过这些坑之后我个人的习惯是在SQL层99%的情况用CASE表达式它清晰、强大、标准只在极简单的等值映射且代码绝不外迁时偶尔用DECODE省几行代码而一旦逻辑复杂到需要变量、循环、异常处理就果断用PL/SQL的IF语句来封装。工具没有好坏只有用得对不对地方。