按字段将该字段值一样的行的另一个字段值合并到一行

时间:2023-02-11 15:01:03

得到如下结果的方式:
DEPTNO EMPLOYEES
------ --------------------------------------------------------------------------------
    10 CLARK,MILLER,KING
    20 SMITH,FORD,ADAMS,SCOTT,JONES
    30 ALLEN,JAMES,TURNER,BLAKE,MARTIN,WARD

--1.第一种方式(采用wm_concat函数)>=10g
SELECT deptno, wm_concat(ename) AS employees FROM emp GROUP BY deptno;

--2.第二种方式(采用listagg)11.2版本采用该函数
SELECT deptno,
       listagg(ename, ',')  within GROUP(ORDER BY ename) AS employees
  FROM emp
 GROUP BY deptno;

 

--3.第三种方式(用户定义的聚集函数)
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 odciaggregatemerge(SELF IN OUT t_string_agg,
                                      ctx2 IN t_string_agg) RETURN NUMBER,

   MEMBER FUNCTION odciaggregateterminate(SELF        IN t_string_agg,
                                          returnvalue OUT VARCHAR2,
                                          flags       IN NUMBER)
      RETURN NUMBER
);


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;


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


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

--4.第四种方式(具体的函数)
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;

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

--减少函数调用次数,先将deptno不同的取出来就可以
SELECT e.deptno, get_employees(e.deptno) AS employees
  FROM (SELECT DISTINCT deptno FROM emp) e;

--5.第五种方式(采用游标)
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;

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;

--减少函数调用次数
SELECT deptno,
       concatenate_list(CURSOR (SELECT e2.ename
                           FROM emp e2
                          WHERE e2.deptno = e1.deptno)) employees
  FROM (SELECT DISTINCT deptno FROM emp) e1;

--6.第六种方式(ROW_NUMBER() and SYS_CONNECT_BY_PATH functions in Oracle 9i)

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;
 
 
--7.第七种方式(COLLECT function in Oracle 10g)
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;

SELECT deptno,
       tab_to_string(CAST(COLLECT(ename) AS t_varchar2_tab)) AS employees
  FROM emp
 GROUP BY deptno;