Change separator of WM_CONCAT function of Oracle 11gR2

后端 未结 4 1379
南方客
南方客 2020-12-11 05:23

Normally, WM_CONCAT is an aggregate function that return values from table separated by comma like here.

Suppose I have a table foo like this:



        
相关标签:
4条回答
  • 2020-12-11 05:28

    You might want to use LISTAGG.

    SELECT col_id, 
           LISTAGG(col_text, '|') WITHIN GROUP (ORDER BY col_text) text
      FROM table1
     GROUP BY col_id
    

    Output:

    | COL_ID |            TEXT |
    ----------------------------
    |    111 | This|a|is|test. |
    

    SQLFiddle

    UPDATE If you need to get distinct text values in a list

    SELECT col_id, 
           LISTAGG(col_text, '|')
             WITHIN GROUP (ORDER BY col_text) text
      FROM 
    (
      SELECT DISTINCT col_id, col_text
        FROM table1
    )
     GROUP BY col_id
    

    SQLFiddle

    0 讨论(0)
  • 2020-12-11 05:32

    The following worked for me on an ORACLE 10.2.0.5.0 database:

    SELECT col_id, replace(wm_concat(col_text), ',', ' ') AS sentence
      FROM foo
     GROUP BY col_id;
    
    0 讨论(0)
  • 2020-12-11 05:41

    Is it possible to change the separator(',') to other characters like '.' or '|' of the WM_CONCAT() function?

    Do not use WM_CONCAT since it is an undocumented feature and it has been removed from the latest 12c version. Any application which has had been relying on wm_concat function will not work once upgraded to 12c. See Why not use WM_CONCAT function in Oracle?

    SQL> select banner from v$version where rownum = 1;
    
    BANNER
    ----------------------------------------------------------------------------
    Oracle Database 12c Enterprise Edition Release 12.1.0.1.0 - 64bit Production
    
    SQL> SELECT object_name
      2  FROM dba_objects
      3  WHERE owner='WMSYS'
      4  AND object_name LIKE 'WM\_%' ESCAPE '\';
    
    OBJECT_NAME
    ----------------------------------------------------------------------------
    WM_REPLICATION_INFO
    WM_RDIFF
    WM_PERIOD
    WM_PERIOD
    WM_OVERLAPS
    WM_MEETS
    WM_LESSTHAN
    WM_LDIFF
    WM_INTERSECTION
    WM_INSTALLATION
    WM_GREATERTHAN
    WM_EVENTS_INFO
    WM_ERROR
    WM_ERROR
    WM_EQUALS
    WM_DDL_UTIL
    WM_DDL_UTIL
    WM_CONTAINS
    WM_COMPRESS_BATCH_SIZES
    WM_COMPRESSIBLE_TABLES
    
    20 rows selected.
    

    You will receive an “invalid identifier” error:

    SQL> SELECT banner FROM v$version;
    
    BANNER
    ----------------------------------------------------------------------------
    Oracle Database 12c Enterprise Edition Release 12.1.0.1.0 - 64bit Production
    PL/SQL Release 12.1.0.1.0 - Production
    CORE    12.1.0.1.0      Production
    TNS for 64-bit Windows: Version 12.1.0.1.0 - Production
    NLSRTL Version 12.1.0.1.0 - Production
    
    SQL> SELECT deptno, wm_concat(ename) FROM emp;
    SELECT deptno, wm_concat(ename) FROM emp
                   *
    ERROR at line 1:
    ORA-00904: "WM_CONCAT": invalid identifier
    

    Therefore, there is no point relying on an undocumented feature which is no more made available in latest versions.

    There are various string aggregation techniques:

    • LISTAGG in 11gR2 and up

    For example,

    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.
    
    • ROW_NUMBER() and SYS_CONNECT_BY_PATH functions in 9i and up

    For example,

    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.
    
    • User-defined aggregate function STRAGG described in AskTom.
    • COLLECT function in 10g and up

    A few good examples by Tim Hall here.

    0 讨论(0)
  • 2020-12-11 05:47

    Problem with LISTAGG that it returns varchar2 and is limited to 4000 bytes

    SELECT LISTAGG(LEVEL, CHR(10)) WITHIN GROUP (ORDER BY NULL)
      FROM Dual
    CONNECT BY LEVEL < 2000
    
    ORA-01489 Result of string concat is too large
    

    I've found one workaround, but it looks ugly and is mutch slower

    SELECT EXTRACT(XMLTYPE('<doc>' || XMLAGG(XMLTYPE('<ln>' || LEVEL || CHR(10) || '</ln>')).GetClobVal() || '</doc>'), '/doc/ln/text()').GetClobVal()
      FROM Dual
    CONNECT BY LEVEL < 2000
    
    0 讨论(0)
提交回复
热议问题