相关链接
前言
碰到此类情况 ,需要将多列数据合并成为一列时,可以采用LISTAGG
聚合函数
NATION|CITY | => NATION|CITIES |
------|---------| => ------|--------------------------|
China |Guangzhou| => China |Beijing,Guangzhou,Shanghai|
China |Shanghai | => Japan |Tokyo |
China |Beijing | => USA |Bostom,New York |
USA |New York |
USA |Bostom |
Japan |Tokyo |
(wc_concat也可以达到同样效果,但测试用的11g版本好像不支持wmsys.wm_concat函数,没有深入研究)
- Oracle 环境
--执行SQL
SELECT * FROM V$VERSION;
--返回结果
BANNER |
----------------------------------------------------------------------------|
Oracle Database 11g Enterprise Edition Release 11.2.0.4.0 - 64bit Production|
PL/SQL Release 11.2.0.4.0 - Production |
CORE 11.2.0.4.0 Production |
TNS for Linux: Version 11.2.0.4.0 - Production |
NLSRTL Version 11.2.0.4.0 - Production |
LISTAGG()语法
- 基础用法 - 参数
- column :待合并字段
- separator : 分隔符,两端需要加单引号
- sortCol:组内排序字段
LISTAGG(${column},'${separator}') WITHIN GROUP(ORDER BY ${sortCol})
注:LISTAGG属于聚合函数,只能和其他聚合函数(SUM,AVG等),或GROUP BY字段一起查询
--执行SQL
SELECT
T.SAL,
LISTAGG(T.ENAME,',') WITHIN GROUP(ORDER BY T.ENAME)
FROM SCOTT.EMP T
--返回结果
org.jkiss.dbeaver.model.sql.DBSQLException: SQL 错误 [937] [42000]: ORA-00937: 不是单组分组函数
- 进阶用法 ( + 开窗函数 )
LISTAGG(${column},'${separator}') WITHIN GROUP(ORDER BY ${sortCol}) OVER (PARTITION BY 分组字段)
实例1 SCOTT.EMP
- 查看表
--执行SQL
SELECT * FROM SCOTT.EMP
--返回结果
EMPNO|ENAME |JOB |MGR |HIREDATE |SAL |COMM|DEPTNO|
-----|------|---------|----|-------------------|----|----|------|
7369|SMITH |CLERK |7902|1980-12-17 00:00:00| 800| | 20|
7499|ALLEN |SALESMAN |7698|1981-02-20 00:00:00|1600| 300| 30|
7521|WARD |SALESMAN |7698|1981-02-22 00:00:00|1250| 500| 30|
7566|JONES |MANAGER |7839|1981-04-02 00:00:00|2975| | 20|
7654|MARTIN|SALESMAN |7698|1981-09-28 00:00:00|1250|1400| 30|
7698|BLAKE |MANAGER |7839|1981-05-01 00:00:00|2850| | 30|
7782|CLARK |MANAGER |7839|1981-06-09 00:00:00|2450| | 10|
7788|SCOTT |ANALYST |7566|1987-04-19 00:00:00|3000| | 20|
7839|KING |PRESIDENT| |1981-11-17 00:00:00|5000| | 10|
7844|TURNER|SALESMAN |7698|1981-09-08 00:00:00|1500| 0| 30|
7876|ADAMS |CLERK |7788|1987-05-23 00:00:00|1100| | 20|
7900|JAMES |CLERK |7698|1981-12-03 00:00:00| 950| | 30|
7902|FORD |ANALYST |7566|1981-12-03 00:00:00|3000| | 20|
7934|MILLER|CLERK |7782|1982-01-23 00:00:00|1300| | 10|
练习1.1
- 不分组,按逗号分隔,组内按员工姓名排序
--执行SQL
SELECT
LISTAGG(T.ENAME,',') WITHIN GROUP(ORDER BY T.ENAME) AS ENAMES
FROM SCOTT.EMP T
--返回结果
ENAMES |
-----------------------------------------------------------------------------------|
ADAMS,ALLEN,BLAKE,CLARK,FORD,JAMES,JONES,KING,MARTIN,MILLER,SCOTT,SMITH,TURNER,WARD|
练习1.2
- 按JOB分组,按逗号分隔,组内按员工姓名(ENAME)排序
--执行SQL
SELECT
T.JOB,
LISTAGG(T.ENAME,',') WITHIN GROUP(ORDER BY T.ENAME) AS ENAMES
FROM SCOTT.EMP T
GROUP BY T.JOB
--返回结果
JOB |ENMAES |
---------|-----------------------------------------------|
ANALYST |FORD,SCOTT |
CLERK |ADAMS,JAMES,MILLER,SMITH |
MANAGER |BLAKE,CLARK,JONES |
PRESIDENT|KING |
SALESMAN |ALLEN,MARTIN,TURNER,WARD |
练习1.3
- 按JOB分组,按逗号分隔,组内按工资(SAL)排序
--执行SQL
SELECT
T.JOB,
LISTAGG(T.ENAME,',') WITHIN GROUP(ORDER BY T.SAL) AS ENMAES,
LISTAGG(T.SAL,',') WITHIN GROUP(ORDER BY T.SAL) AS SALS
FROM SCOTT.EMP T
GROUP BY T.JOB
--返回结果
JOB |ENMAES |SALS |
---------|---------------------------------------------|-------------------------------------------|
ANALYST |FORD,SCOTT |3000,3000 |
CLERK |SMITH,JAMES,ADAMS,MILLER |800,950,1100,1300 |
MANAGER |CLARK,BLAKE,JONES |2450,2850,2975 |
PRESIDENT|KING |5000 |
SALESMAN |MARTIN,WARD,TURNER,ALLEN |1250,1250,1500,1600 |
实例2 虚表DUAL
- 查看表
--执行SQL
SELECT 'China' nation ,'Guangzhou' city FROM dual UNION ALL
SELECT 'China' nation ,'Shanghai' city FROM dual UNION ALL
SELECT 'China' nation ,'Beijing' city FROM dual UNION ALL
SELECT 'USA' nation ,'New York' city FROM dual UNION ALL
SELECT 'USA' nation ,'Bostom' city FROM dual UNION ALL
SELECT 'Japan' nation ,'Tokyo' city FROM dual
--返回结果
NATION|CITY |
------|---------|
China |Guangzhou|
China |Shanghai |
China |Beijing |
USA |New York |
USA |Bostom |
Japan |Tokyo |
练习2.1
- 按国家分组,按逗号分隔,组内按城市名(CITY)称排序
--执行SQL
WITH temp AS(
SELECT 'China' nation ,'Guangzhou' city FROM dual UNION ALL
SELECT 'China' nation ,'Shanghai' city FROM dual UNION ALL
SELECT 'China' nation ,'Beijing' city FROM dual UNION ALL
SELECT 'USA' nation ,'New York' city FROM dual UNION ALL
SELECT 'USA' nation ,'Bostom' city FROM dual UNION ALL
SELECT 'Japan' nation ,'Tokyo' city FROM dual
)
SELECT
nation,
LISTAGG(city,',') WITHIN GROUP (ORDER BY city) AS CITIES
FROM temp
GROUP BY nation
--返回结果
NATION|CITIES |
------|-----------------------------------------|
China |Beijing,Guangzhou,Shanghai |
Japan |Tokyo |
USA |Bostom,New York |
练习2.2 LISTAGG 高阶用法
- LISTAGG + 开窗函数 OVER PARTITION BY
--执行SQL
WITH temp AS(
SELECT 'China' nation ,'Guangzhou' city FROM dual UNION ALL
SELECT 'China' nation ,'Shanghai' city FROM dual UNION ALL
SELECT 'China' nation ,'Beijing' city FROM dual UNION ALL
SELECT 'USA' nation ,'New York' city FROM dual UNION ALL
SELECT 'USA' nation ,'Bostom' city FROM dual UNION ALL
SELECT 'Japan' nation ,'Tokyo' city FROM dual
)
SELECT
nation,
LISTAGG(city,',') WITHIN GROUP (ORDER BY city) OVER (PARTITION BY nation) AS CITIES
FROM temp
ORDER BY NATION
--返回结果
NATION|CITIES |
------|--------------------------|
China |Beijing,Guangzhou,Shanghai|
China |Beijing,Guangzhou,Shanghai|
China |Beijing,Guangzhou,Shanghai|
Japan |Tokyo |
USA |Bostom,New York |
USA |Bostom,New York |
--执行SQL
WITH temp AS(
SELECT 'China' nation ,'Guangzhou' city FROM dual UNION ALL
SELECT 'China' nation ,'Shanghai' city FROM dual UNION ALL
SELECT 'China' nation ,'Beijing' city FROM dual UNION ALL
SELECT 'USA' nation ,'New York' city FROM dual UNION ALL
SELECT 'USA' nation ,'Bostom' city FROM dual UNION ALL
SELECT 'Japan' nation ,'Tokyo' city FROM dual
)
SELECT DISTINCT
nation,
LISTAGG(city,',') WITHIN GROUP (ORDER BY city) OVER (PARTITION BY nation) AS CITIES
FROM temp
ORDER BY NATION
--返回结果
NATION|CITIES |
------|--------------------------|
China |Beijing,Guangzhou,Shanghai|
Japan |Tokyo |
USA |Bostom,New York |
参考文章
20/08/21
M