• Oracle 自定义函数实现列转行效果


    在 Oracle 领域,我相信一说到列转行大部分人都会立马想到 WM_CONCAT 函数,我觉得主要是因为该函数比较实用。但事实上 WM_CONCAT 并非官方公开函数,使用会存在一定的风险;函数返回值的格式比较单一(只能用逗号分割);返回值的长度也限制。

    《.Net程序员学用Oracle系列(20):层次查询(CONNECT BY)》一文中,详细讲解了 WM_CONCAT 函数的用法。如果不用 WM_CONCAT 函数又该如何实现列转行呢?当数据类别比较少的时候,通过 CASE 判断或 UNION ALL 查询也能实现效果,但问题就是写法太死板,不过一般也没人会这么干。本文接下来会介绍两个自定义函数分别来实现列转行的聚合效果。

    第一个自定义函数主要是辅以 COLLECT 函数来实现的,我在回答一个园友的问题时列出了实现步骤,查看该问题。下面是我重新整理后的版本:

    第 1 步(创建类型 type_table_string,用于转换 COLLECT 函数的返回值):

    CREATE OR REPLACE TYPE type_table_string IS TABLE OF VARCHAR2(4000);
    

    第 2 步(创建函数 fn_to_string,用于将 type_table_string 类型转换成普通字符串):

    CREATE OR REPLACE FUNCTION fn_to_string(
      p_str_tab IN type_table_string,
      p_separator IN VARCHAR2 DEFAULT ','
    )
    RETURN VARCHAR2 IS
      v_ret_str VARCHAR2(4000);
    BEGIN
      FOR i IN 1..p_str_tab.COUNT LOOP
        v_ret_str:=v_ret_str||p_separator||p_str_tab(i);
      END LOOP;
      RETURN LTRIM(v_ret_str,p_separator);
    END;
    

    调用方法:

    SELECT t.dept_code,fn_to_string(CAST(COLLECT(t.staff_name) AS type_table_string),'|') staff_names
    FROM demo.t_staff t GROUP BY t.dept_code;
    

    调用结果:

    DEPT_CODE                        STAFF_NAMES
    -------------------------------- ----------------------------------------------
    010101                           小明|小强|王二
    010102                           小林|小萨
    010103                           韩三|小玲
    010104                           小梅|小燕
    010201                           小军|小芳|小红
    010202                           小飞
    

    第二个自定义函数在百度中输入“zh_concat”就能找到一堆的参考,我有尝试寻找 zh_concat 函数的出处,但网上的翻版实在太多,而且大部分比较无耻(没写出处),结果就是我没能找到该函数的出处,因此在这里我也没法儿给出 zh_concat 函数的出处了。

    我找到的那些有关 zh_concat 函数的帖子内容普遍比较混乱,于是我决定整理出一个更加清晰、整洁和美观的版本,以便阅读和理解,具体如下:

    第 1 步(创建类型 type_concat 的定义):

    CREATE OR REPLACE TYPE type_concat
    AUTHID CURRENT_USER AS OBJECT(
      v_result_string VARCHAR2(4000),
      STATIC FUNCTION odciAggregateInitialize(
        concat IN OUT type_concat) RETURN NUMBER,
      MEMBER FUNCTION odciAggregateIterate(
        SELF IN OUT type_concat,str IN VARCHAR2) RETURN NUMBER,
      MEMBER FUNCTION odciAggregateTerminate(
        SELF IN type_concat,return_value OUT VARCHAR2,flags IN NUMBER) RETURN NUMBER,
      MEMBER FUNCTION odciAggregateMerge(
        SELF IN OUT type_concat,concat IN type_concat) RETURN NUMBER
    );
    

    第 2 步(创建类型 type_concat 的 body):

    CREATE OR REPLACE TYPE BODY type_concat
    IS
      STATIC FUNCTION odciAggregateInitialize(concat IN OUT type_concat)
      RETURN NUMBER IS
      BEGIN
        concat := type_concat(NULL);
        RETURN ODCICONST.SUCCESS;
      END;
      
      MEMBER FUNCTION odciAggregateIterate(SELF IN OUT type_concat,str IN VARCHAR2)
      RETURN NUMBER IS
      BEGIN
        IF SELF.v_result_string IS NOT NULL THEN
          SELF.v_result_string := SELF.v_result_string||','||str;
        ELSE
          SELF.v_result_string := str;
        END IF;
        RETURN ODCICONST.SUCCESS;
      END;
      
      MEMBER FUNCTION odciAggregateTerminate(SELF IN type_concat,return_value OUT VARCHAR2,flags IN NUMBER)
      RETURN NUMBER IS
      BEGIN
        return_value := SELF.v_result_string;
        RETURN ODCICONST.SUCCESS;
      END;
      
      MEMBER FUNCTION odciAggregateMerge(SELF IN OUT type_concat,concat IN type_concat)
      RETURN NUMBER IS
      BEGIN
        IF concat.v_result_string IS NOT NULL THEN
          SELF.v_result_string := SELF.v_result_string||','||concat.v_result_string;
        END IF;
        RETURN ODCICONST.SUCCESS;
      END;
    END;
    

    第 3 步(创建函数 fn_concat,可替代 WM_CONCAT):

    CREATE OR REPLACE FUNCTION fn_concat(str VARCHAR2)
    RETURN VARCHAR2 AGGREGATE USING type_concat;
    

    调用方法:

    SELECT t.dept_code,fn_concat(t.staff_name) staff_names FROM demo.t_staff t GROUP BY t.dept_code;
    

    调用结果:

    DEPT_CODE                             STAFF_NAMES
    ------------------------------------- ----------------------------------------------------
    010101                                小明,小强,王二
    010102                                小林,小萨
    010103                                韩三,小玲
    010104                                小梅,小燕
    010201                                小军,小芳,小红
    010202                                小飞
    

    说明一:上例中的 AUTHID CURRENT_USER 是权限控制的关键字,表示调用者权限,即当前用户。默认为 AUTHID DEFINER,表示定义者权限,即模式拥有者。

    说明二:将 type_concat 中 v_result_string 和 return_value 的类型改为 CLOB 类型,并将 fn_concat 的返回值类型也改为 CLOB,就成了 CLOB 版的 fn_concat 了。

    本文链接http://www.cnblogs.com/hanzongze/p/oracle-wm_concat.html
    版权声明:本文为博客园博主 韩宗泽 原创,作者保留署名权!欢迎通过转载、演绎或其它传播方式来使用本文,但必须在明显位置给出作者署名和本文链接!个人博客,能力有限,若有不当之处,敬请批评指正,谢谢!

  • 相关阅读:
    win7一关机电脑就蓝屏
    win10系统怎么查自己电脑的IP地址[系统天地]
    Win7如何设置任务管理器的快捷键?[系统天地]
    Win10系统下安全登录密码忘记了怎么办【系统天地】
    Win7系统很卡应该如何解决?[系统天地]
    Win7系统运行慢如何解决?——系统天地
    Win7如何解决开机应用程序无法正常启动
    win10系统怎么切换独立显卡
    程序员的自我修养
    SQL学习笔记
  • 原文地址:https://www.cnblogs.com/hanzongze/p/oracle-wm_concat.html
Copyright © 2020-2023  润新知