如何根据 Oracle 11g+ 中聚合元素的数量改变 LISTAGG() 的结果?

How to vary result of LISTAGG() depending on number of aggregated elements in Oracle 11g+?

如何根据聚合元素的数量在 LISTAGG() 中打印不同的输出?

是否可以在没有额外的 COUNT(*) 查询的情况下获取聚合元素的数量?

有一个示例 DDL:

create table shepherds (
  SHEPHERD_ID NUMBER(19),
  SHEPHERD_NAME VARCHAR2(50 CHAR)
);

create table sheeps (
  SHEEP_ID VARCHAR2(10 CHAR),
  SHEEP_NAME VARCHAR2(50 CHAR),
  SHEEP_SHEPHERD_ID NUMBER(19)
);

-- insert shepherds
insert into shepherds VALUES (111, 'Asher');
insert into shepherds VALUES (222, 'Joseph');
insert into shepherds VALUES (333, 'Nicodemus');

-- first shepherd (one sheep)
insert into sheeps VALUES ('A', 'Mark', 111); 

-- second shepherd (two sheeps)
insert into sheeps VALUES ('A', 'Andres', 222);
insert into sheeps VALUES ('B', 'Jeffrey', 222);

-- third shepherd (three sheeps)
insert into sheeps VALUES ('B', 'Jeffrey', 333);
insert into sheeps VALUES ('A', 'Andres', 333);
insert into sheeps VALUES ('D', 'Andres', 333);

现在我想按以下方式显示所有羊名以换行符分隔的牧羊人:

SELECT
  SHEPHERD_NAME,
  (SELECT
     listagg(SHEEP_ID || ': ' || SHEEP_NAME, CHR(10)) WITHIN GROUP (ORDER BY SHEEP_ID)
   FROM SHEEPS
   WHERE SHEEP_SHEPHERD_ID = SHEPHERD_ID)
FROM SHEPHERDS;

结果是:http://sqlfiddle.com/#!4/881a7/3

但是,对于那些只有一只羊的牧羊人,我想把羊的ID字母隐藏起来。

我尝试了以下方法:

SELECT
  SHEPHERD_NAME,
  (SELECT
     listagg(
       CASE WHEN COUNT(*) > 1 THEN SHEEP_ID || ': ' ELSE '' END
       || SHEEP_NAME, CHR(10)) WITHIN GROUP (ORDER BY SHEEP_ID)
   FROM SHEEPS
   WHERE SHEEP_SHEPHERD_ID = SHEPHERD_ID)
FROM SHEPHERDS;

但是,我收到错误消息:

ORA-00978: nested group function without GROUP BY

http://sqlfiddle.com/#!4/881a7/7

如果只有一个元素要聚合,是否可以 return 来自 LISTAGG() 的不同字符串?

如何在不降低 Oracle 11g 或更高版本中的查询性能的情况下检测聚合元素的数量?

子查询中的条件表达式应该满足您的要求:

SELECT sh.SHEPHERD_NAME, 
        (SELECT (CASE WHEN COUNT(*) = 1 THEN MAX(s.SHEEP_NAME)
                      ELSE LISTAGG(s.SHEEP_ID || ': ' || s.SHEEP_NAME, CHR(10)) WITHIN GROUP (ORDER BY s.SHEEP_ID)
                 END) as SHEEPS
         FROM SHEEPS s
         WHERE s.SHEEP_SHEPHERD_ID = sh.SHEPHERD_ID
       ) as SHEEPS
FROM SHEPHERDS sh;

Here 是一个 db<>fiddle.

没有子查询的解决方案使用简单的GROUP BYCOUNT(*) = 1来区分羊数和两个不同的LISTAGG语句

SELECT
  s.SHEPHERD_NAME,
  case when count(*) = 1 then 
   listagg(SHEEP_NAME, CHR(10)) WITHIN GROUP (ORDER BY SHEEP_ID)  
  else 
   listagg(SHEEP_ID || ': ' || SHEEP_NAME, CHR(10)) WITHIN GROUP (ORDER BY SHEEP_ID) end as SHEEPS
FROM SHEPHERDS s
JOIN SHEEPS sh on s.SHEPHERD_ID = sh.SHEEP_SHEPHERD_ID
GROUP BY s.SHEPHERD_NAME /* add SHEPHERD_ID in GROUP BY if the name is not unique */

returns

SHEPHERD_NAME, SHEEPS
Asher          Mark
Joseph         A: Andres
               B: Jeffrey
Nicodemus      A: Andres
               B: Jeffrey
               D: Andres