聊聊PL/SQL条件编译

本文介绍了PL/SQL中的条件编译,如何根据环境和数据库版本编译不同的代码。通过预处理控制指令和查询指令,如$IF、$THEN、$ELSE、PLSQL_LINE等,实现不同环境的差异化编译。示例中展示了如何在开发环境与生产环境间切换调试语句,以及针对不同数据库版本选择合适的函数。条件编译简化了代码维护,避免了因环境差异导致的问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

      说说PL/SQL一个比较冷门但是很强大的东西——条件编译,顾名思义,条件编译就是根据不同的条件编译PL/SQL应用而无需移除源码中的文本。比如,根据不同的数据库版本,编译不同的源码;在开发环境中用于debug的语句,当在生产环境部署时不编译该部分语句。

预处理控制指令

      预处理控制指令的基本模式为$plsql_identitier,用于在PL/SQL编译之前处理PL/SQL源码。其指令如下:

  • $IF
  • $THEN
  • $ELSE
  • $ELIF
  • $ERROR

通过这些指令我们就可以完成编译的选择以及错误指令,基本语法如下:

$IF boolean_static_expression $THEN
   text
[ $ELSIF boolean_static_expression $THEN
   text
   $ERROR 'unsupported database release' $END
]...
[ $ELSE
   text
$END
]

查询指令

      既然是条件编译,那么很关键的一点就是如何获取编译环境的信息,这就是查询指令的作用。

预定义查询指令

      查询指令的基本语法为:$$name。预定义的name如下:
* PLSQL_LINE:值为其出现的PL/SQL单元的行数。
* PLSQL_UNIT:值为该PL/SQL单元的名字。
* PLSQL_UNIT_OWNER:值为该编译单元所属用户,对于匿名PL/SQL块,值为NULL。
* PLSQL_UNIT_TYPE:值为该PL/SQL单元的类型,ANONYMOUS BLOCK, FUNCTION, PACKAGE, PACKAGE BODY, PROCEDURE, TRIGGER, TYPE, 或者 TYPE BODY。
* plsql_compilation_parameter: PL/SQL编译参数,PLSCOPE_SETTINGS、PLSQL_CCFLAGS、PLSQL_CODE_TYPE、PLSQL_OPTIMIZE_LEVEL等。

需要注意的是查询指令只能与NULL进行比较。下面是一个简单的示例:

SQL> CREATE OR REPLACE PROCEDURE p IS
  2    i PLS_INTEGER;
  3  BEGIN
  4    i := $$PLSQL_LINE;
  5    DBMS_OUTPUT.PUT_LINE('i = ' || i);
  6    DBMS_OUTPUT.PUT_LINE('PLSQL_LINE = ' || $$PLSQL_LINE);
  7    DBMS_OUTPUT.PUT_LINE('PLSQL_UNIT = ' || $$PLSQL_UNIT);
  8    DBMS_OUTPUT.PUT_LINE('PLSQL_UNIT_OWNER = ' || $$PLSQL_UNIT_OWNER);
  9    DBMS_OUTPUT.PUT_LINE('PLSQL_UNIT_TYPE = ' || $$PLSQL_UNIT_TYPE);
 10  END;
 11  /

输出:

PLSQL_LINE = 4
PLSQL_UNIT = 6
PLSQL_UNIT_OWNER = CENZHGL
PLSQL_UNIT_TYPE = PROCEDURE

为查询指令赋值

      我们可以通过PLSQL_CCFLAGS编译参数为查询指令赋值,其基本模式为key:value,value的值只能是BOOLEAN或者PLS_INTEGER。如下:

ALTER SESSION SET PLSQL_CCFLAGS = 'name1:value1, name2:value2, ... namen:valuen'

PLSQL_CCFLAGS可以为ORACLE预定义的查询指令赋值,但是ORACLE并不建议这么做,这样容易让人迷惑。

示例-为不同的数据库版本编译不同的代码

      ORACLE提供了DBMS_DB_VERSION用于在控制指令中处理数据库版本问题。其提供了以下静态常量:
* VERSION:值为PLS_INTEGER类型,标识当前数据库版本。
* RELEASE: 值为PLS_INTEGER类型, 标识当前数据库的RELEASE NUMBER。
* VER_LE_v:一个BOOLEAN的常数值,判断数据库版本是否比v小。
* VER_LE_v_r: BOOLEAN的常数值,判断数据库版本号是否比v小,release number是否比r小。

oracle列转行中有两个函数:WM_CONCAT和LISTAGG。12c开始,ORACLE摒弃了WM_CONCAT(可通过解锁wmsys用户,然后创建包、包体、函数和同义词使其在12c中正常使用),推荐使用LISTAGG。LISTAGG是在11G2中才开始提供了。那么我们为了在11g2之后使用LISTAGG,之前的版本使用WM_CONCAT,条件编译就可以帮助我们同一份代码在不同的数据库版本中有不同的实现。

CREATE OR REPLACE PROCEDURE Comp_dif_db_version IS
BEGIN
    WITH TMP AS
    (SELECT regexp_substr('A,B,C,D,E,F,G','[^,]+',1,LEVEL) cr FROM DUAL
        CONNECT BY REGEXP_INSTR('A,B,C,D,E,F,G','[^,]+',1,LEVEL)>0);

    $IF DBMS_DB_VERSION.VER_LE_11_2 $THEN
        SELECT WM_CONCAT(CR) FROM TMP;
    $ELSE
        SELECT LISTAGG(CR,',') WITHIN GROUP(ORDER BY 1) FROM TMP;
    $END
END;
/

执行CALL DBMS_PREPROCESSOR.PRINT_POST_PROCESSED_SOURCE('PROCEDURE','CENYLFW','COMP_DIF_DB_VERSION');命令可以看到结果如下:

PROCEDURE Comp_dif_db_version IS
   RESULT_STR VARCHAR2(500);
BEGIN
  WITH TMP AS
   (SELECT regexp_substr('A,B,C,D,E,F,G', '[^,]+', 1, LEVEL) cr
      FROM DUAL
    CONNECT BY REGEXP_INSTR('A,B,C,D,E,F,G', '[^,]+', 1, LEVEL) > 0)
  SELECT LISTAGG(CR, ',') WITHIN GROUP(ORDER BY 1) INTO RESULT_STR FROM TMP;

END;
示例-区分开发环境与生产环境

      在做PL/SQL程序开发的时候,有时候我们会在程序中使用DBMS_OUTPUT.PUT_LINE等语句来调试追踪程序的执行,但是在生产环境中不屏蔽这些语句有可能会导致一些问题,手动去删除源码不仅麻烦还不能保证所有都删除。这时候我们可以使用条件编译,分别在生产环境和开发环境编译不同代码。

ALTER SESSION SET PLSQL_CCFLAGS = 'dev_env:TRUE';

create or replace procedure PRC_UPDATE_BRWYID is
  sql_script clob;
  I_LOG      INTEGER;
begin
  FOR TABLES IN (SELECT TABLE_NAME
                   FROM USER_TAB_COLS A
                  WHERE A.column_name = 'BRWYID') LOOP
    begin
      sql_script := 'UPDATE CENYLFW.' || TABLES.TABLE_NAME ||
                    ' A SET BRWYID=NVL((SELECT BRWYID FROM CENYLFW.TB_YLFW_BRWYID_UPDATE B WHERE A.GRBSH = B.KH AND A.GRBSLX = B.KLX AND A.YLJGDM=B.YLJGDM),''-1'')';

      $IF $$dev_env $THEN
        DBMS_OUTPUT.PUT_LINE(sql_script);
      $END

      EXECUTE IMMEDIATE SQL_SCRIPT;
      COMMIT;
      I_LOG := CENZHGL.fun_write_job_log('finish update ' ||
                                         TABLES.TABLE_NAME || '.BRWYID!');
    exception
      when others then
        I_LOG := CENZHGL.fun_write_job_log('update ' || TABLES.TABLE_NAME ||
                                           ' BRWYID faild!:->' || Sqlcode || '->' ||
                                           substrb(Sqlerrm, 0, 500));
    end;
  END LOOP;
end PRC_UPDATE_BRWYID;
/

执行CALL DBMS_PREPROCESSOR.PRINT_POST_PROCESSED_SOURCE('PROCEDURE','CENYLFW','COMP_DIF_DB_VERSION');即可看到编译后的结果。有了条件编译,我们就不需要在开发的时候添加调试代码,在开发环境的时候又去部署。

总结

      合理利用条件编译可以为我们生产开发带来很大的方便。它可以让我们方便的在新版本的数据库中使用新的特性,在旧的数据库中屏蔽这些新的特性,只需要在编码时多加几句控制指令即可,避免了去增删源码而可能产生的BUG。我们也可以使用PLSQL_CCFLAGS编译参数来为查询指令赋值,用来区分开发环境和生产环境,在生产环境中屏蔽开发环境中的调试语句。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值