`
wang4674890
  • 浏览: 89372 次
  • 性别: Icon_minigender_2
  • 来自: 厦门
社区版块
存档分类
最新评论

oracle--多行转为一行的连接手段

阅读更多
今天遇到将多行转为一行的一个操作,多谢oracle开发板的 wildwave 提供了比较通用的解决办法,同时也将自己搜到的这方面资料整理如下,多是用于连接列值的。
String集聚连接技术

需要将多行转换为一行,例子如下:

    基础数据:
        DEPTNO ENAME
    ---------- ----------
            20 SMITH
            30 ALLEN
            30 WARD
            20 JONES
            30 MARTIN
            30 BLAKE
            10 CLARK
            20 SCOTT
            10 KING
            30 TURNER
            20 ADAMS
            30 JAMES
            20 FORD
            10 MILLER

    预期输出:

        DEPTNO EMPLOYEES
    ---------- --------------------------------------------------
            10 CLARK,KING,MILLER
            20 SMITH,FORD,ADAMS,SCOTT,JONES
            30 ALLEN,BLAKE,MARTIN,TURNER,JAMES,WARD

    * LISTAGG分析函数(11g Release 2)
    * WM_CONCAT内建函数
    * 自定义函数
    * 使用Ref Cursor实现通用函数
    * 用户自定义聚集函数
    * ROW_NUMBER()和SYS_CONNECT_BY_PATH函数(Oracle 9i)
    * COLLECT函数(Oracle 10g)

LISTAGG分析函数(11g Release 2)

Oracle 11g Release 2介绍了LISTAGG 函数,使得聚集连接字符串变得很容易。并且允许使用我们指定连接串中的字段顺序。使用LISTAGG如下:

    COLUMN employees FORMAT A50

    SELECT deptno, LISTAGG(ename, ',') WITHIN GROUP (ORDER BY ename) AS employees
    FROM   emp
    GROUP BY deptno;

        DEPTNO EMPLOYEES
    ---------- --------------------------------------------------
            10 CLARK,KING,MILLER
            20 ADAMS,FORD,JONES,SCOTT,SMITH
            30 ALLEN,BLAKE,JAMES,MARTIN,TURNER,WARD

    3 rows selected.

WM_CONCAT内建函数

如果你的Oracle不是11g Release 2,但是支持WM_CONCAT函数,那么解决上面的问题同样是小菜一碟,使用WM_CONCAT 函数G如下:

    COLUMN employees FORMAT A50

    SELECT deptno, wm_concat(ename) AS employees
    FROM   emp
    GROUP BY deptno;

        DEPTNO EMPLOYEES
    ---------- --------------------------------------------------
            10 CLARK,KING,MILLER
            20 SMITH,FORD,ADAMS,SCOTT,JONES
            30 ALLEN,BLAKE,MARTIN,TURNER,JAMES,WARD

    3 rows selected.

自定义函数

另一个方法是自定义一个函数解决问题。get_employees对于给定部门返回一组员工:

    CREATE OR REPLACE FUNCTION get_employees (p_deptno  in  emp.deptno%TYPE)
      RETURN VARCHAR2
    IS
      l_text  VARCHAR2(32767) := NULL;
    BEGIN
      FOR cur_rec IN (SELECT ename FROM emp WHERE deptno = p_deptno) LOOP
        l_text := l_text || ',' || cur_rec.ename;
      END LOOP;
      RETURN LTRIM(l_text, ',');
    END;
    /
    SHOW ERRORS

    COLUMN employees FORMAT A50

    SELECT deptno,
           get_employees(deptno) AS employees
    FROM   emp
    GROUP by deptno;

        DEPTNO EMPLOYEES
    ---------- --------------------------------------------------
            10 CLARK,KING,MILLER
            20 SMITH,JONES,SCOTT,ADAMS,FORD
            30 ALLEN,WARD,MARTIN,BLAKE,TURNER,JAMES

    3 rows selected.

为了改善性能减少函数调用,我们预先过滤行数。.

    COLUMN employees FORMAT A50

    SELECT e.deptno,
           get_employees(e.deptno) AS employees
    FROM   (SELECT DISTINCT deptno
            FROM   emp) e;

        DEPTNO EMPLOYEES
    ---------- --------------------------------------------------
            10 CLARK,KING,MILLER
            20 SMITH,JONES,SCOTT,ADAMS,FORD
            30 ALLEN,WARD,MARTIN,BLAKE,TURNER,JAMES
           
    3 rows selected.

使用Ref Cursor实现通用函数

另一个可替代的方法是使用游标变量写一个函数来连接行值。基本和上面一样,只是传入的是一个游标,所以使得它更通用:.

    CREATE OR REPLACE FUNCTION concatenate_list (p_cursor IN  SYS_REFCURSOR)
      RETURN  VARCHAR2
    IS
      l_return  VARCHAR2(32767);
      l_temp    VARCHAR2(32767);
    BEGIN
      LOOP
        FETCH p_cursor
        INTO  l_temp;
        EXIT WHEN p_cursor%NOTFOUND;
        l_return := l_return || ',' || l_temp;
      END LOOP;
      RETURN LTRIM(l_return, ',');
    END;
    /
    SHOW ERRORS

使用如下:

    COLUMN employees FORMAT A50

    SELECT e1.deptno,
           concatenate_list(CURSOR(SELECT e2.ename FROM emp e2 WHERE e2.deptno = e1.deptno)) employees
    FROM   emp e1
    GROUP BY e1.deptno;

        DEPTNO EMPLOYEES
    ---------- --------------------------------------------------
            10 CLARK,KING,MILLER
            20 SMITH,JONES,SCOTT,ADAMS,FORD
            30 ALLEN,WARD,MARTIN,BLAKE,TURNER,JAMES

    3 rows selected.

同样的,为了减少函数调用可以预先顾虑一些行。.

    COLUMN employees FORMAT A50

    SELECT deptno,
           concatenate_list(CURSOR(SELECT e2.ename FROM emp e2 WHERE e2.deptno = e1.deptno)) employees
    FROM   (SELECT DISTINCT deptno
            FROM emp) e1;

        DEPTNO EMPLOYEES
    ---------- --------------------------------------------------
            10 CLARK,KING,MILLER
            20 SMITH,JONES,SCOTT,ADAMS,FORD
            30 ALLEN,WARD,MARTIN,BLAKE,TURNER,JAMES

    3 rows selected.

用户自定义聚集函数

如果你不想使用内置的函数,你可以自定义聚集函数:

    CREATE OR REPLACE TYPE t_string_agg AS OBJECT
    (
      g_string  VARCHAR2(32767),

      STATIC FUNCTION ODCIAggregateInitialize(sctx  IN OUT  t_string_agg)
        RETURN NUMBER,

      MEMBER FUNCTION ODCIAggregateIterate(self   IN OUT  t_string_agg,
                                           value  IN      VARCHAR2 )
         RETURN NUMBER,

      MEMBER FUNCTION ODCIAggregateTerminate(self         IN   t_string_agg,
                                             returnValue  OUT  VARCHAR2,
                                             flags        IN   NUMBER)
        RETURN NUMBER,

      MEMBER FUNCTION ODCIAggregateMerge(self  IN OUT  t_string_agg,
                                         ctx2  IN      t_string_agg)
        RETURN NUMBER
    );
    /
    SHOW ERRORS


    CREATE OR REPLACE TYPE BODY t_string_agg IS
      STATIC FUNCTION ODCIAggregateInitialize(sctx  IN OUT  t_string_agg)
        RETURN NUMBER IS
      BEGIN
        sctx := t_string_agg(NULL);
        RETURN ODCIConst.Success;
      END;

      MEMBER FUNCTION ODCIAggregateIterate(self   IN OUT  t_string_agg,
                                           value  IN      VARCHAR2 )
        RETURN NUMBER IS
      BEGIN
        SELF.g_string := self.g_string || ',' || value;
        RETURN ODCIConst.Success;
      END;

      MEMBER FUNCTION ODCIAggregateTerminate(self         IN   t_string_agg,
                                             returnValue  OUT  VARCHAR2,
                                             flags        IN   NUMBER)
        RETURN NUMBER IS
      BEGIN
        returnValue := RTRIM(LTRIM(SELF.g_string, ','), ',');
        RETURN ODCIConst.Success;
      END;

      MEMBER FUNCTION ODCIAggregateMerge(self  IN OUT  t_string_agg,
                                         ctx2  IN      t_string_agg)
        RETURN NUMBER IS
      BEGIN
        SELF.g_string := SELF.g_string || ',' || ctx2.g_string;
        RETURN ODCIConst.Success;
      END;
    END;
    /
    SHOW ERRORS


    CREATE OR REPLACE FUNCTION string_agg (p_input VARCHAR2)
    RETURN VARCHAR2
    PARALLEL_ENABLE AGGREGATE USING t_string_agg;
    /
    SHOW ERRORS

使用如下:

    COLUMN employees FORMAT A50

    SELECT deptno, string_agg(ename) AS employees
    FROM   emp
    GROUP BY deptno;

        DEPTNO EMPLOYEES
    ---------- --------------------------------------------------
            10 CLARK,KING,MILLER
            20 SMITH,FORD,ADAMS,SCOTT,JONES
            30 ALLEN,BLAKE,MARTIN,TURNER,JAMES,WARD

    3 rows selected.

ROW_NUMBER()和SYS_CONNECT_BY_PATH函数(Oracle 9i)

使用 ROW_NUMBER() 和SYS_CONNECT_BY_PATH 实现:

    SELECT deptno,
           LTRIM(MAX(SYS_CONNECT_BY_PATH(ename,','))
           KEEP (DENSE_RANK LAST ORDER BY curr),',') AS employees
    FROM   (SELECT deptno,
                   ename,
                   ROW_NUMBER() OVER (PARTITION BY deptno ORDER BY ename) AS curr,
                   ROW_NUMBER() OVER (PARTITION BY deptno ORDER BY ename) -1 AS prev
            FROM   emp)
    GROUP BY deptno
    CONNECT BY prev = PRIOR curr AND deptno = PRIOR deptno
    START WITH curr = 1;

        DEPTNO EMPLOYEES
    ---------- --------------------------------------------------
            10 CLARK,KING,MILLER
            20 ADAMS,FORD,JONES,SCOTT,SMITH
            30 ALLEN,BLAKE,JAMES,MARTIN,TURNER,WARD

    3 rows selected.

COLLECT函数(Oracle 10g)

使用COLLECT函数,这个需要一个关联数组:

    CREATE OR REPLACE TYPE t_varchar2_tab AS TABLE OF VARCHAR2(4000);
    /

    CREATE OR REPLACE FUNCTION tab_to_string (p_varchar2_tab  IN  t_varchar2_tab,
                                              p_delimiter     IN  VARCHAR2 DEFAULT ',') RETURN VARCHAR2 IS
      l_string     VARCHAR2(32767);
    BEGIN
      FOR i IN p_varchar2_tab.FIRST .. p_varchar2_tab.LAST LOOP
        IF i != p_varchar2_tab.FIRST THEN
          l_string := l_string || p_delimiter;
        END IF;
        l_string := l_string || p_varchar2_tab(i);
      END LOOP;
      RETURN l_string;
    END tab_to_string;
    /

使用如下:

    COLUMN employees FORMAT A50

    SELECT deptno,
           tab_to_string(CAST(COLLECT(ename) AS t_varchar2_tab)) AS employees
    FROM   emp
    GROUP BY deptno;
          
        DEPTNO EMPLOYEES
    ---------- --------------------------------------------------
            10 CLARK,KING,MILLER
            20 SMITH,JONES,SCOTT,ADAMS,FORD
            30 ALLEN,WARD,MARTIN,BLAKE,TURNER,JAMES
           
    3 rows selected.

分享到:
评论

相关推荐

    oracle多行转为字符串总结

    介绍了将多行转为字符串的三种方案,并比较了三种方案的执行效率. 1.sys_connect_by_path + start with ... connect by ... prior + 分析函数 2.自定义Function/SP 3.使用 Oracle 10g 内置函数 wmsys.wm_concat

    oracle将以逗号分隔字符串转多行

    在Oracle数据库中,将逗号分隔的字符串转换为多行是常见的数据处理需求,尤其在需要对每个分隔项进行单独操作时。这个过程通常涉及到字符串处理函数,如`REGEXP_SUBSTR`和`REPLACE`,以及层次查询结构`CONNECT BY`。...

    SQL 语句 将一个表中用特殊字符分割的字段转换成多行数据.docx

    在数据库中,经常会遇到将一个字段中的特殊字符分割的字符串转换成多行数据的情况。这是一个常见的需求,但是网上提供的解决方案往往非常复杂,难以理解和实现。为了解决这个问题,我们可以创建一个字符串分割函数,...

    DB2 SQL 实现行转列,列转行

    DB2 SQL 通过函数(CONCAT/POSSTR/LOCATE)实现行转列,列转行 可以按照标点把多列转换为一行,多行转换为一列

    oracle 行转列

    在关系型数据库中,数据通常是按行存储的,即每一行代表一个实体的多个属性。但有时我们需要将某些特定属性的值转换为列,以便于比较或分析。例如,一个销售数据表可能包含产品ID、销售日期和销售额三列,如果想要按...

    oracle手册,详解SQL用法

    单行函数主要作用于一行数据的一个字段,例如字符函数,它们可以对字符串进行操作,如`LOWER()`将字符串转为小写,`UPPER()`转为大写,`INSTR()`查找子串的位置。数字函数如`ROUND()`用于四舍五入,`TRUNC()`用于...

    oracle笔记

    单行函数针对单行数据进行操作,对每一行输入返回一个单独的结果;而聚合函数则是对多行数据进行处理,返回单一的汇总结果。 3.1 单行函数 单行函数包括字符函数、数字函数、转换函数、日期函数和正则表达式函数。 ...

    Oracle的列转行问题

    这样,一行数据被拆分为多行,实现了列转行的效果。 具体的SQL语句如下: ```sql SELECT a.f1, b.fid, DECODE(b.fid, '数据 1', a.qty1, '数据 2', a.qty2, '数据 3', a.qty3) FROM f_distribution a, (SELECT '...

    oracle_lhr_行列互换总结

    例如,原表有三个列c1、c2、c3,转为行后,每行将包含一列的名字和对应的数据。可以通过UNION ALL查询语句来实现列转行。在使用UNION ALL时,需注意空值的处理,可以在查询中加入WHERE子句来过滤掉不需要的空值。 2...

    Oracle数据库开发规范.pdf

    5. 语句书写规范:当一条SQL语句跨越多行书写时,每一行的开始应当是关键字,并且关键字应与第一行左对齐。如果实在无法从关键字开始分行,则应保证分行处与上一行被分行的同类代码最左边对齐。 6. INSERT语句的...

    Oracle公司内部数据库培训资料

    单行函数在处理数据时,针对每一行数据返回一个单独的结果,如转换函数,它们可以对字符、数值和日期进行转换。而多行函数则可能涉及到整个数据集的计算,例如聚合函数SUM、AVG等,它们会处理多行数据并返回一个总结...

    Oracle函数与查询.pptx

    OLAP函数用于复杂的分析操作,如LEAD和LAG函数获取当前行前后行的值,RANK、DENSE_RANK和ROW_NUMBER用于行号分配,FIRST_VALUE和LAST_VALUE获取分组内第一行或最后一行的值。 7. **查询优化**: 在实际查询中,...

    sql高级进阶

    - 将结果集反向转置为一列:将多行数据合并为单个字段。 - 抑制结果集中的重复值:使用DISTINCT关键字。 - 利用“行转列”进行计算:在转换后的数据上进行分析计算。 - 给数据分组:使用GROUP BY子句对数据进行...

    数据库行列转换算法

    行转列则是将多行数据转换为一列或多列。同样有多种方法: - **AGGREGATE FUNCTION**:如DECODE或CASE表达式与MAX、MIN、SUM等聚合函数结合使用,适用于8i,9i,10g 及以后版本。例如,将表`t_row_col`的行转换回原列...

    列转行小工具

    例如,假设我们有一个销售数据表,包含产品ID、销售日期和销售额三个字段,如果想按产品ID汇总每个产品的年度销售额,可以使用PIVOT将销售日期转换为年份的列,将销售额从行数据转为列数据。 2. UNPIVOT操作:...

Global site tag (gtag.js) - Google Analytics