Oracle의 LISTAGG는 고유 한 값을 반환합니다.


94

LISTAGGOracle 에서 기능 을 사용하려고합니다 . 해당 열에 대한 고유 한 값만 얻고 싶습니다. 함수 나 프로 시저를 만들지 않고 고유 한 값만 얻을 수있는 방법이 있습니까?

  col1 col2 Created_by
   1 2 스미스 
   요한 1 서 2 
   1 3 아제이 
   1 4 램 
   1 5 잭 

col1과 LISTAGGcol2 를 선택해야합니다 (열 3은 고려되지 않음). 이렇게하면 다음과 같은 결과가 나타납니다 LISTAGG. [2,2,3,4,5]

여기서 중복 된 '2'를 제거해야합니다. col1에 대한 col2의 고유 한 값만 필요합니다.



샘플에서 예상되는 출력 (행)을 표시 할 수 있습니까? col1에 대해 둘 이상의 값이 있는지 확인하고 싶습니까?
a_horse_with_no_name jul.

LISTAGG의 예상 출력은 [2,3,4,5]입니다. 두 번째 '2'는 제거해야합니다. 그리고 내 테이블에는 1000 개 이상의 행이 있습니다.
Priyanth 2012

col1에 대해 둘 이상의 값이 있는지 확인하고 싶습니까?
a_horse_with_no_name

코드는 다음과 같습니다 .- SELECT col1, LISTAGG (col2, ',') within group (order by col2) FROM table T WHERE .... 그래서, shoukd는 col1에 해당하는 col2의 모든 고유 값을 표시합니다. 반점.
Priyanth

답변:


77

19c 이상 :

select listagg(distinct the_column, ',') within group (order by the_column)
from the_table

18c 이하 :

select listagg(the_column, ',') within group (order by the_column)
from (
   select distinct the_column 
   from the_table
) t

더 많은 열이 필요한 경우 다음과 같은 항목을 찾을 수 있습니다.

select col1, listagg(col2, ',') within group (order by col2)
from (
  select col1, 
         col2,
         row_number() over (partition by col1, col2 order by col1) as rn
  from foo
  order by col1,col2
)
where rn = 1
group by col1;

2
내가 생각했던 것과 비슷합니다. 경우 listagg쿼리에서 유일하게 집계 함수이며,이 수행해야합니다. 그러나 다른 집계 함수와 결합하는 것은 더 까다 롭습니다.
Andriy M

예. 내 쿼리는 이와 비슷합니다.
Priyanth

1
@a_horse_with_no_name : 위의 select 문은 나에게 중복 값을 제공합니다. 중복을 제거하고 싶습니다. col1 col2 작성자 1 2 Smith 1 2 John 1 3 Ajay 1 4 Ram 1 5 Jack col1과 col2의 LISTAGG를 선택해야합니다 (열 3은 고려되지 않음). 내가 그렇게하는 동안 나는 결과 od LISTAGG :-> [2,2,3,4,5] 여기에서 중복 '2'를 제거해야합니다. col1에 대한 col2의 고유 한 값 만 필요합니다. .
Priyanth

@a_horse_with_no_name : 코드를 시도했는데 아래와 같은 오류 메시지가 나타납니다. ORA-01489 : 문자열 연결 결과가 너무 깁니다. 01489. 00000- "문자열 연결 결과가 너무 깁니다."* 원인 : 문자열 연결 결과가 최대 값보다 깁니다. 크기.
Priyanth

@Priyanth : 그럼 당신은 운이 좋지 않습니다. 총 길이가 4000 바이트를 초과하므로 Oracle은이를 처리 할 수 ​​없습니다. 애플리케이션 코드에서 집계를 수행해야합니다.
a_horse_with_no_name jul.

47

문제를 해결하는 방법은 다음과 같습니다.

select  
      regexp_replace(
    '2,2,2.1,3,3,3,3,4,4' 
     ,'([^,]+)(,\1)*(,|$)', '\1\3')

from dual

보고

2,2.1,3,4

Oracle 19C에서 여기에 내장되어 있습니다 .

18C 이전부터 그룹 내에서 시도해보십시오. 여기를 참조 하십시오.

그렇지 않으면 정규 표현식을 사용하십시오.

아래 답변 :

select col1, 

regexp_replace(
    listagg(
     col2 , ',') within group (order by col2)  -- sorted
    ,'([^,]+)(,\1)*(,|$)', '\1\3') )
   from tableX
where rn = 1
group by col1; 

참고 : 위의 내용은 대부분의 경우 작동합니다. 목록을 정렬해야하며 데이터에 따라 모든 후행 및 선행 공백을 잘라야 할 수 있습니다.

그룹에 20 개 이상의 문자열 크기 또는 큰 문자열 크기의 항목이 많은 경우 '문자열 연결 결과가 너무 깁니다'라는 오라클 문자열 크기 제한에 부딪 힐 수 있습니다.

oracle 12cR2에서이 오류를 억제 할 수 있습니다 . 여기를 참조 하십시오 . 또는 각 그룹의 구성원에 최대 수를 입력하십시오. 첫 번째 구성원 만 나열해도 괜찮은 경우에만 작동합니다. 매우 긴 변수 문자열이 있으면 작동하지 않을 수 있습니다. 실험을해야합니다.

select col1,

case 
    when count(col2) < 100 then 
       regexp_replace(
        listagg(col2, ',') within group (order by col2)
        ,'([^,]+)(,\1)*(,|$)', '\1\3')

    else
    'Too many entries to list...'
end

from sometable
where rn = 1
group by col1;

(그렇게 간단하지) 또 다른 해결책은 희망 오라클 문자열 크기 제한을 피하기 위해 - 문자열 크기가이 게시물에 4000 감사로 제한됩니다 여기 에서 user3465996

select col1  ,
    dbms_xmlgen.convert(  -- HTML decode
    dbms_lob.substr( -- limit size to 4000 chars
    ltrim( -- remove leading commas
    REGEXP_REPLACE(REPLACE(
         REPLACE(
           XMLAGG(
             XMLELEMENT("A",col2 )
               ORDER BY col2).getClobVal(),
             '<A>',','),
             '</A>',''),'([^,]+)(,\1)*(,|$)', '\1\3'),
                  ','), -- remove leading XML commas ltrim
                      4000,1) -- limit to 4000 string size
                      , 1)  -- HTML.decode
                       as col2
 from sometable
where rn = 1
group by col1;

V1-일부 테스트 사례-FYI

regexp_replace('2,2,2.1,3,3,4,4','([^,]+)(,\1)+', '\1')
-> 2.1,3,4 Fail
regexp_replace('2 ,2 ,2.1,3 ,3 ,4 ,4 ','([^,]+)(,\1)+', '\1')
-> 2 ,2.1,3,4 Success  - fixed length items

V2-항목 내에 포함 된 항목 예 : 2,21

regexp_replace('2.1,1','([^,]+)(,\1)+', '\1')
-> 2.1 Fail
regexp_replace('2 ,2 ,2.1,1 ,3 ,4 ,4 ','(^|,)(.+)(,\2)+', '\1\2')
-> 2 ,2.1,1 ,3 ,4  -- success - NEW regex
 regexp_replace('a,b,b,b,b,c','(^|,)(.+)(,\2)+', '\1\2')
-> a,b,b,c fail!

v3-정규식 Igor 감사합니다! 모든 경우에 작동합니다.

select  
regexp_replace('2,2,2.1,3,3,4,4','([^,]+)(,\1)*(,|$)', '\1\3') ,
---> 2,2.1,3,4 works
regexp_replace('2.1,1','([^,]+)(,\1)*(,|$)', '\1\3'),
--> 2.1,1 works
regexp_replace('a,b,b,b,b,c','([^,]+)(,\1)*(,|$)', '\1\3')
---> a,b,c works

from dual

3
공정한 결과이지만 그렇게 간단하지는 않습니다. 심각한 데이터 크기로 ORA-01489: result of string concatenation is too long.
Pero

1
간단하지만 매우 매력적인 솔루션이라고 부르지 않습니다. 대체 문자열뿐만 아니라 검색 문자열에서 일치 번호를 사용할 수 있다는 것을 몰랐습니다. 화려한.
Peter Krassoi 2014 년

1
주의 할 점으로이 방법을 사용하려면 중복 된 값이 연속되도록 값을 정렬해야합니다. 그렇지 않으면 실패합니다. 그러나 단순함이 좋습니다! 그리고 저는이 방법을 제 특별한 경우에 사용하고 있습니다. 감사!
StewS2

2
슈퍼 단순은 3 회 이상 작동하지 않습니다! 예를 들면이 a,b,b,b,b,c될 것 a,b,b,c:-( (오라클 11.2)
안드레아스 디트리히

4
@AndreasDietrich-다음 솔루션은 항상 올바른 것 같습니다.regexp_replace(your_string, '([^,]+)(,\1)*(,|$)', '\1\3')
Egor Skriptunoff

10

문서화되지 않은 wm_concat기능을 사용할 수 있습니다 .

select col1, wm_concat(distinct col2) col2_list 
from tab1
group by col1;

이 함수는 dbms_lob.substrclob를 varchar2로 변환 하는 데 사용할 수있는 경우 clob 열을 반환합니다 .


15
아니, 사용하지 마세요.
Koshinae 2014 년

1
이것이 바로 내가 필요로하는 것이었고, 쿼리를 외부 쿼리에 래핑하는 대신 기존 집계 쿼리 내에서 완벽하게 작동했습니다. 사용에있어 문제점은 무엇입니까 wm_concat(distinct x)?
Ehryk 2015

1
문서화되지 않았고 12c에 존재하지 않기 때문입니다. 그러나 어쨌든 이전 버전에서는 이것이 최선의 방법이라고 생각합니다.
Kemalettin Erbakırcı 2015

1
감사합니다 @ kemalettinerbakırcı! @thg 문서화되지 않은 것이 있으면 부작용이 무엇인지, 문서가 문서화 된 기능에 대해 설명하는 다른 종류의 내용을 알 수 없다는 점을 고려해야합니다. 당신은 그것을 블랙 박스로 사용하고 민속에 기초하여 어떤 레버가 어떤 역할을하는지 알고 있습니다.
Koshinae

4
사용하지 마십시오 wm_concat. Oracle에서 WM_CONCAT 함수를 사용하지 않는 이유를 참조하십시오 . .
Lalit Kumar B

7

먼저 값을 그룹화하여이 문제를 극복 한 다음 listagg로 또 다른 집계를 수행합니다. 이 같은:

select a,b,listagg(c,',') within group(order by c) c, avg(d)
from (select a,b,c,avg(d)
      from   table
      group by (a,b,c))
group by (a,b)

하나의 전체 테이블 액세스, 비교적 복잡한 쿼리로 확장하기 쉽습니다.


6

이 변환을 여러 열에 적용하려는 경우 a_horse_with_no_name의 솔루션을 확장했습니다.

SELECT * FROM
(SELECT LISTAGG(GRADE_LEVEL, ',') within group(order by GRADE_LEVEL) "Grade Levels" FROM (select distinct GRADE_LEVEL FROM Students) t)                     t1,
(SELECT LISTAGG(ENROLL_STATUS, ',') within group(order by ENROLL_STATUS) "Enrollment Status" FROM (select distinct ENROLL_STATUS FROM Students) t)          t2,
(SELECT LISTAGG(GENDER, ',') within group(order by GENDER) "Legal Gender Code" FROM (select distinct GENDER FROM Students) t)                               t3,
(SELECT LISTAGG(CITY, ',') within group(order by CITY) "City" FROM (select distinct CITY FROM Students) t)                                                  t4,
(SELECT LISTAGG(ENTRYCODE, ',') within group(order by ENTRYCODE) "Entry Code" FROM (select distinct ENTRYCODE FROM Students) t)                             t5,
(SELECT LISTAGG(EXITCODE, ',') within group(order by EXITCODE) "Exit Code" FROM (select distinct EXITCODE FROM Students) t)                                 t6,
(SELECT LISTAGG(LUNCHSTATUS, ',') within group(order by LUNCHSTATUS) "Lunch Status" FROM (select distinct LUNCHSTATUS FROM Students) t)                     t7,
(SELECT LISTAGG(ETHNICITY, ',') within group(order by ETHNICITY) "Race Code" FROM (select distinct ETHNICITY FROM Students) t)                              t8,
(SELECT LISTAGG(CLASSOF, ',') within group(order by CLASSOF) "Expected Graduation Year" FROM (select distinct CLASSOF FROM Students) t)                     t9,
(SELECT LISTAGG(TRACK, ',') within group(order by TRACK) "Track Code" FROM (select distinct TRACK FROM Students) t)                                         t10,
(SELECT LISTAGG(GRADREQSETID, ',') within group(order by GRADREQSETID) "Graduation ID" FROM (select distinct GRADREQSETID FROM Students) t)                 t11,
(SELECT LISTAGG(ENROLLMENT_SCHOOLID, ',') within group(order by ENROLLMENT_SCHOOLID) "School Key" FROM (select distinct ENROLLMENT_SCHOOLID FROM Students) t)       t12,
(SELECT LISTAGG(FEDETHNICITY, ',') within group(order by FEDETHNICITY) "Federal Race Code" FROM (select distinct FEDETHNICITY FROM Students) t)                         t13,
(SELECT LISTAGG(SUMMERSCHOOLID, ',') within group(order by SUMMERSCHOOLID) "Summer School Key" FROM (select distinct SUMMERSCHOOLID FROM Students) t)                               t14,
(SELECT LISTAGG(FEDRACEDECLINE, ',') within group(order by FEDRACEDECLINE) "Student Decl to Prov Race Code" FROM (select distinct FEDRACEDECLINE FROM Students) t)          t15

이것은 Oracle Database 11g Enterprise Edition Release 11.2.0.2.0-64 비트 프로덕션입니다.
DISTINCT 및 ORDER 방법이 없기 때문에 STRAGG를 사용할 수 없습니다.

관심있는 모든 열을 추가하기 때문에 성능이 선형 적으로 확장됩니다. 위의 작업은 77K 행에 대해 3 초가 걸렸습니다. 단 한 번의 롤업의 경우 .172 초입니다. 한 번의 패스로 테이블의 여러 열을 구별하는 방법이 있습니다.


6

여러 열에 걸쳐 고유 한 값을 원하고 정렬 순서를 제어하고 사라지는 문서화되지 않은 함수를 사용하지 않고 하나 이상의 전체 테이블 스캔을 원하지 않는 경우이 구성이 유용 할 수 있습니다.

with test_data as 
(
      select 'A' as col1, 'T_a1' as col2, '123' as col3 from dual
union select 'A', 'T_a1', '456' from dual
union select 'A', 'T_a1', '789' from dual
union select 'A', 'T_a2', '123' from dual
union select 'A', 'T_a2', '456' from dual
union select 'A', 'T_a2', '111' from dual
union select 'A', 'T_a3', '999' from dual
union select 'B', 'T_a1', '123' from dual
union select 'B', 'T_b1', '740' from dual
union select 'B', 'T_b1', '846' from dual
)
select col1
     , (select listagg(column_value, ',') within group (order by column_value desc) from table(collect_col2)) as col2s
     , (select listagg(column_value, ',') within group (order by column_value desc) from table(collect_col3)) as col3s
from 
(
select col1
     , collect(distinct col2) as collect_col2
     , collect(distinct col3) as collect_col3
from test_data
group by col1
);

1
"union"을 "union all"로 바꾸면 시간을 더 절약 할 수 있습니다.
burkay

4

"구별 한"부분을 만드는 전용 함수를 만드는 것은 어떻습니까?

create or replace function listagg_distinct (t in str_t, sep IN VARCHAR2 DEFAULT ',') 
  return VARCHAR2
as 
  l_rc VARCHAR2(4096) := '';
begin
  SELECT listagg(val, sep) WITHIN GROUP (ORDER BY 1)
    INTO l_rc
    FROM (SELECT DISTINCT column_value val FROM table(t));
  RETURN l_rc;
end;
/

그런 다음이를 사용하여 집계를 수행합니다.

SELECT col1, listagg_distinct(cast(collect(col_2) as str_t ), ', ')
  FROM your_table
  GROUP BY col_1;

4

문자열 길이 문제를 해결하기 위해 XMLAGG유사 listagg하지만 clob을 반환하는 것을 사용할 수 있습니다 .

그런 다음을 사용하여 구문 분석 regexp_replace하고 고유 한 값을 가져온 다음를 사용하여 다시 문자열로 변환 할 수 dbms_lob.substr()있습니다. 엄청난 양의 고유 값이있는 경우에도 여전히 공간이 부족하지만 많은 경우 아래 코드가 작동합니다.

사용하는 구분 기호를 변경할 수도 있습니다. 제 경우에는 ''대신 '-'를 원했지만 원하는 경우 내 코드에서 대시를 바꾸고 쉼표를 사용할 수 있어야합니다.

select col1,
    dbms_lob.substr(ltrim(REGEXP_REPLACE(REPLACE(
         REPLACE(
           XMLAGG(
             XMLELEMENT("A",col2)
               ORDER BY col2).getClobVal(),
             '<A>','-'),
             '</A>',''),'([^-]*)(-\1)+($|-)', 
           '\1\3'),'-'), 4000,1) as platform_mix
from table

dbms_xmlgen.convert (string, 1)을 호출하여 &-> & amp 변환을 제거하는 것이 좋습니다. 내 게시물보기 링크를
ozmike

3

DECODE 대 CASE를 사용하여 @a_horse_with_no_name의 row_number () 기반 접근 방식에 대한 @YoYo의 수정을 추가로 구체화합니다 ( 여기에서 보았습니다 ). @Martin Vrbovsky 도이 사례 접근 방식에 대한 답변을 가지고 있음을 알 수 있습니다.

select
  col1, 
  listagg(col2, ',') within group (order by col2) AS col2_list,
  listagg(col3, ',') within group (order by col3) AS col3_list,
  SUM(col4) AS col4
from (
  select
    col1, 
    decode(row_number() over (partition by col1, col2 order by null),1,col2) as col2,
    decode(row_number() over (partition by col1, col3 order by null),1,col3) as col3
  from foo
)
group by col1;

2

지원 다가오는 오라클 19C DISTINCT와 함께 LISTAGG.

DISTINCT 옵션이있는 LISTAGG :

이 기능은 19c와 함께 제공됩니다.

SQL> select deptno, listagg (distinct sal,', ') within group (order by sal)  
  2  from scott.emp  
  3  group by deptno;  

편집하다:

Oracle 19C LISTAGG DISTINCT

LISTAGG 집계 함수는 이제 새 DISTINCT 키워드를 사용하여 중복 제거를 지원합니다. LISTAGG 집계 함수는 ORDER BY 식에 따라 쿼리의 각 그룹에 대한 행을 정렬 한 다음 값을 단일 문자열로 연결합니다. 새 DISTINCT 키워드를 사용하면 단일 문자열로 연결하기 전에 지정된 표현식에서 중복 값을 제거 할 수 있습니다. 따라서 집계 LISTAGG 함수를 사용하기 전에 고유 한 값을 찾기 위해 복잡한 쿼리 처리를 만들 필요가 없습니다. DISTINCT 옵션을 사용하면 LISTAGG 함수 내에서 중복 값을 제거하는 처리를 직접 수행 할 수 있습니다. 결과는 더 간단하고 빠르며 효율적인 SQL입니다.


0

PARTITION BY 절을 사용할 생각이 있습니까? 이 쿼리에서 응용 프로그램 서비스 및 액세스 목록을 가져 오는 것이 저에게 효과적이었습니다.

SELECT DISTINCT T.APP_SVC_ID, 
       LISTAGG(RTRIM(T.ACCESS_MODE), ',') WITHIN GROUP(ORDER BY T.ACCESS_MODE) OVER(PARTITION BY T.APP_SVC_ID) AS ACCESS_MODE 
  FROM APP_SVC_ACCESS_CNTL T 
 GROUP BY T.ACCESS_MODE, T.APP_SVC_ID

NDA에 대한 where 절을 잘라 내야했지만 아이디어를 얻었습니다.


이 쿼리가 LISTAGG. T.ACCESS_MODE그룹화 한 이후 행당 하나만있는 것 같습니까?
jpmc26

0

나는 이것이 도움이 될 수 있다고 생각합니다-중복되는 경우 열 값을 NULL로 CASE하면 LISTAGG 문자열에 추가되지 않습니다.

with test_data as 
(
      select 1 as col1, 2 as col2, 'Smith' as created_by from dual
union select 1, 2, 'John' from dual
union select 1, 3, 'Ajay' from dual
union select 1, 4, 'Ram' from dual
union select 1, 5, 'Jack' from dual
union select 2, 5, 'Smith' from dual
union select 2, 6, 'John' from dual
union select 2, 6, 'Ajay' from dual
union select 2, 6, 'Ram' from dual
union select 2, 7, 'Jack' from dual
)
SELECT col1  ,
      listagg(col2 , ',') within group (order by col2 ASC) AS orig_value,
      listagg(CASE WHEN rwn=1 THEN col2 END , ',') within group (order by col2 ASC) AS distinct_value
from 
    (
    select row_number() over (partition by col1,col2 order by 1) as rwn, 
           a.*
    from test_data a
    ) a
GROUP BY col1   

결과 :

COL1  ORIG         DISTINCT
1   2,2,3,4,5   2,3,4,5
2   5,6,6,6,7   5,6,7

0

listagg ()는 NULL 값을 무시하므로 첫 번째 단계에서 lag () 함수를 사용하여 이전 레코드의 값이 동일한 지 여부를 분석 할 수 있습니다 (예, NULL, 그렇지 않으면 '새 값').

WITH tab AS 
(           
          SELECT 1 as col1, 2 as col2, 'Smith' as created_by FROM dual
UNION ALL SELECT 1 as col1, 2 as col2, 'John'  as created_by FROM dual
UNION ALL SELECT 1 as col1, 3 as col2, 'Ajay'  as created_by FROM dual
UNION ALL SELECT 1 as col1, 4 as col2, 'Ram'   as created_by FROM dual
UNION ALL SELECT 1 as col1, 5 as col2, 'Jack'  as created_by FROM dual
)
SELECT col1
     , CASE 
       WHEN lag(col2) OVER (ORDER BY col2) = col2 THEN 
         NULL 
       ELSE 
         col2 
       END as col2_with_nulls
     , created_by
  FROM tab;

결과

      COL1 COL2_WITH_NULLS CREAT
---------- --------------- -----
         1               2 Smith
         1                 John
         1               3 Ajay
         1               4 Ram
         1               5 Jack

두 번째 2는 NULL로 대체됩니다. 이제 listagg ()를 사용하여 SELECT를 래핑 할 수 있습니다.

WITH tab AS 
(           
          SELECT 1 as col1, 2 as col2, 'Smith' as created_by FROM dual
UNION ALL SELECT 1 as col1, 2 as col2, 'John'  as created_by FROM dual
UNION ALL SELECT 1 as col1, 3 as col2, 'Ajay'  as created_by FROM dual
UNION ALL SELECT 1 as col1, 4 as col2, 'Ram'   as created_by FROM dual
UNION ALL SELECT 1 as col1, 5 as col2, 'Jack'  as created_by FROM dual
)
SELECT listagg(col2_with_nulls, ',') WITHIN GROUP (ORDER BY col2_with_nulls) col2_list
  FROM ( SELECT col1
              , CASE WHEN lag(col2) OVER (ORDER BY col2) = col2 THEN NULL ELSE col2 END as col2_with_nulls
              , created_by
           FROM tab );

결과

COL2_LIST
---------
2,3,4,5

여러 열에서도이 작업을 수행 할 수 있습니다.

WITH tab AS 
(           
          SELECT 1 as col1, 2 as col2, 'Smith' as created_by FROM dual
UNION ALL SELECT 1 as col1, 2 as col2, 'John'  as created_by FROM dual
UNION ALL SELECT 1 as col1, 3 as col2, 'Ajay'  as created_by FROM dual
UNION ALL SELECT 1 as col1, 4 as col2, 'Ram'   as created_by FROM dual
UNION ALL SELECT 1 as col1, 5 as col2, 'Jack'  as created_by FROM dual
)
SELECT listagg(col1_with_nulls, ',') WITHIN GROUP (ORDER BY col1_with_nulls) col1_list
     , listagg(col2_with_nulls, ',') WITHIN GROUP (ORDER BY col2_with_nulls) col2_list
     , listagg(created_by, ',')      WITHIN GROUP (ORDER BY created_by) created_by_list
  FROM ( SELECT CASE WHEN lag(col1) OVER (ORDER BY col1) = col1 THEN NULL ELSE col1 END as col1_with_nulls
              , CASE WHEN lag(col2) OVER (ORDER BY col2) = col2 THEN NULL ELSE col2 END as col2_with_nulls
              , created_by
           FROM tab );

결과

COL1_LIST COL2_LIST CREATED_BY_LIST
--------- --------- -------------------------
1         2,3,4,5   Ajay,Jack,John,Ram,Smith

0

RegEx 교체를 통해 할 수 있습니다. 다음은 예입니다.

-- Citations Per Year - Cited Publications main query. Includes list of unique associated core project numbers, ordered by core project number.
SELECT ptc.pmid AS pmid, ptc.pmc_id, ptc.pub_title AS pubtitle, ptc.author_list AS authorlist,
  ptc.pub_date AS pubdate,
  REGEXP_REPLACE( LISTAGG ( ppcc.admin_phs_org_code || 
    TO_CHAR(ppcc.serial_num,'FM000000'), ',') WITHIN GROUP (ORDER BY ppcc.admin_phs_org_code || 
    TO_CHAR(ppcc.serial_num,'FM000000')),
    '(^|,)(.+)(,\2)+', '\1\2')
  AS projectNum
FROM publication_total_citations ptc
  JOIN proj_paper_citation_counts ppcc
    ON ptc.pmid = ppcc.pmid
   AND ppcc.citation_year = 2013
  JOIN user_appls ua
    ON ppcc.admin_phs_org_code = ua.admin_phs_org_code
   AND ppcc.serial_num = ua.serial_num
   AND ua.login_id = 'EVANSF'
GROUP BY ptc.pmid, ptc.pmc_id, ptc.pub_title, ptc.author_list, ptc.pub_date
ORDER BY pmid;

또한 여기에 게시 됨 : Oracle-고유 한 Listagg 값


0

다음과 같이 생성 된 listagg_clob 함수를 사용합니다.

create or replace package list_const_p
is
list_sep varchar2(10) := ',';
end list_const_p;
/
sho err

create type listagg_clob_t as object(
v_liststring varchar2(32767),
v_clob clob,
v_templob number,

static function ODCIAggregateInitialize(
sctx IN OUT listagg_clob_t
) return number,
member function ODCIAggregateIterate(
self IN OUT listagg_clob_t, value IN varchar2
) return number,
member function ODCIAggregateTerminate(
self IN OUT listagg_clob_t, returnValue OUT clob, flags IN number
) return number,
member function ODCIAggregateMerge(
self IN OUT listagg_clob_t, ctx2 IN OUT listagg_clob_t
) return number
);
/
sho err

create or replace type body listagg_clob_t is

static function ODCIAggregateInitialize(sctx IN OUT listagg_clob_t)
return number is
begin
sctx := listagg_clob_t('', '', 0);
return ODCIConst.Success;
end;

member function ODCIAggregateIterate(
self IN OUT listagg_clob_t,
value IN varchar2
) return number is
begin
if nvl(lengthb(v_liststring),0) + nvl(lengthb(value),0) <= 4000 then
self.v_liststring:=self.v_liststring || value || list_const_p.list_sep;
else
if self.v_templob = 0 then
dbms_lob.createtemporary(self.v_clob, true, dbms_lob.call);
self.v_templob := 1;
end if;
dbms_lob.writeappend(self.v_clob, length(self.v_liststring), v_liststring);
self.v_liststring := value || list_const_p.list_sep;
end if;
return ODCIConst.Success;
end;

member function ODCIAggregateTerminate(
self IN OUT listagg_clob_t,
returnValue OUT clob,
flags IN number
) return number is
begin
if self.v_templob != 0 then
dbms_lob.writeappend(self.v_clob, length(self.v_liststring), self.v_liststring);
dbms_lob.trim(self.v_clob, dbms_lob.getlength(self.v_clob) - 1);
else
self.v_clob := substr(self.v_liststring, 1, length(self.v_liststring) - 1);
end if;
returnValue := self.v_clob;
return ODCIConst.Success;
end;

member function ODCIAggregateMerge(self IN OUT listagg_clob_t, ctx2 IN OUT listagg_clob_t) return number is
begin
if ctx2.v_templob != 0 then
if self.v_templob != 0 then
dbms_lob.append(self.v_clob, ctx2.v_clob);
dbms_lob.freetemporary(ctx2.v_clob);
ctx2.v_templob := 0;
else
self.v_clob := ctx2.v_clob;
self.v_templob := 1;
ctx2.v_clob := '';
ctx2.v_templob := 0;
end if;
end if;
if nvl(lengthb(self.v_liststring),0) + nvl(lengthb(ctx2.v_liststring),0) <= 4000 then
self.v_liststring := self.v_liststring || ctx2.v_liststring;
ctx2.v_liststring := '';
else
if self.v_templob = 0 then
dbms_lob.createtemporary(self.v_clob, true, dbms_lob.call);
self.v_templob := 1;
end if;
dbms_lob.writeappend(self.v_clob, length(self.v_liststring), self.v_liststring);
dbms_lob.writeappend(self.v_clob, length(ctx2.v_liststring), ctx2.v_liststring);
self.v_liststring := '';
ctx2.v_liststring := '';
end if;
return ODCIConst.Success;
end;
end;
/
sho err

CREATE or replace FUNCTION listagg_clob (input varchar2) RETURN clob
PARALLEL_ENABLE AGGREGATE USING listagg_clob_t;
/
sho err 


0

정규 표현식을 사용하여이를 처리하는 함수를 작성했습니다. in 매개 변수는 다음과 같습니다. 1) listagg 호출 자체 2) 구분 기호의 반복

create or replace function distinct_listagg
  (listagg_in varchar2,
   delimiter_in varchar2)

   return varchar2
   as
   hold_result varchar2(4000);
   begin

   select rtrim( regexp_replace( (listagg_in)
      , '([^'||delimiter_in||']*)('||
      delimiter_in||'\1)+($|'||delimiter_in||')', '\1\3'), ',')
      into hold_result
      from dual;

return hold_result;

end;

이제이 작업을 수행 할 때마다 정규식을 반복 할 필요가 없습니다.

select distinct_listagg(
                       listagg(myfield,', ') within group (order by 1),
                       ', '
                       )
     from mytable;

0

연결된 값의 특정 순서가 필요하지 않고 구분 기호가 쉼표 일 수있는 경우 다음을 수행 할 수 있습니다.

select col1, stragg(distinct col2)
  from table
 group by col1

0

나는 이것의 DISTINCT 버전을 neded했고 이것을 작동시켰다.

RTRIM(REGEXP_REPLACE(
                       (value, ', ') WITHIN GROUP( ORDER BY value)), 
                            '([^ ]+)(, \1)+','\1'),', ') 

0

한 가지 성가신 측면 LISTAGG은 연결된 문자열의 총 길이가 4000자를 초과하면 ( VARCHAR2SQL에서는 제한 ) 아래 오류가 발생하여 Oracle 버전 12.1까지 관리하기가 어렵다는 것입니다.

ORA-01489 : 스트링 연결 결과가 너무 깁니다

12cR2에 추가 된 새로운 기능은의 ON OVERFLOW절입니다 LISTAGG. 이 절을 포함하는 쿼리는 다음과 같습니다.

SELECT pid, LISTAGG(Desc, ' ' on overflow truncate) WITHIN GROUP (ORDER BY seq) AS desc
FROM B GROUP BY pid;

위의 경우 출력은 4000 자로 제한되지만 ORA-01489오류 는 발생 하지 않습니다 .

다음은 ON OVERFLOW조항 의 추가 옵션 중 일부입니다 .

  • ON OVERFLOW TRUNCATE 'Contd..' : 이것은 'Contd..'문자열의 끝에 표시 됩니다 (기본값은 ...).
  • ON OVERFLOW TRUNCATE '' : 종료 문자열없이 4000자를 표시합니다.
  • ON OVERFLOW TRUNCATE WITH COUNT: 종료 문자 뒤의 끝에 총 문자 수가 표시됩니다. 예 :- ' ...(5512)'
  • ON OVERFLOW ERROR: 오류 LISTAGG와 함께 실패 할 것으로 예상되는 경우 ORA-01489(어쨌든 기본값 임).

0

이 저장 기능을 구현했습니다.

CREATE TYPE LISTAGG_DISTINCT_PARAMS AS OBJECT (ELEMENTO VARCHAR2(2000), SEPARATORE VARCHAR2(10));

CREATE TYPE T_LISTA_ELEMENTI AS TABLE OF VARCHAR2(2000);

CREATE TYPE T_LISTAGG_DISTINCT AS OBJECT (

    LISTA_ELEMENTI T_LISTA_ELEMENTI,
        SEPARATORE VARCHAR2(10),

    STATIC FUNCTION ODCIAGGREGATEINITIALIZE(SCTX  IN OUT            T_LISTAGG_DISTINCT) 
                    RETURN NUMBER,

    MEMBER FUNCTION ODCIAGGREGATEITERATE   (SELF  IN OUT            T_LISTAGG_DISTINCT, 
                                            VALUE IN                    LISTAGG_DISTINCT_PARAMS ) 
                    RETURN NUMBER,

    MEMBER FUNCTION ODCIAGGREGATETERMINATE (SELF         IN     T_LISTAGG_DISTINCT,
                                            RETURN_VALUE OUT    VARCHAR2, 
                                            FLAGS        IN     NUMBER      )
                    RETURN NUMBER,

    MEMBER FUNCTION ODCIAGGREGATEMERGE       (SELF               IN OUT T_LISTAGG_DISTINCT,
                                                                                        CTX2                 IN         T_LISTAGG_DISTINCT    )
                    RETURN NUMBER
);

CREATE OR REPLACE TYPE BODY T_LISTAGG_DISTINCT IS 

    STATIC FUNCTION ODCIAGGREGATEINITIALIZE(SCTX IN OUT T_LISTAGG_DISTINCT) RETURN NUMBER IS 
    BEGIN
                SCTX := T_LISTAGG_DISTINCT(T_LISTA_ELEMENTI() , ',');
        RETURN ODCICONST.SUCCESS;
    END;

    MEMBER FUNCTION ODCIAGGREGATEITERATE(SELF IN OUT T_LISTAGG_DISTINCT, VALUE IN LISTAGG_DISTINCT_PARAMS) RETURN NUMBER IS
    BEGIN

                IF VALUE.ELEMENTO IS NOT NULL THEN
                        SELF.LISTA_ELEMENTI.EXTEND;
                        SELF.LISTA_ELEMENTI(SELF.LISTA_ELEMENTI.LAST) := TO_CHAR(VALUE.ELEMENTO);
                        SELF.LISTA_ELEMENTI:= SELF.LISTA_ELEMENTI MULTISET UNION DISTINCT SELF.LISTA_ELEMENTI;
                        SELF.SEPARATORE := VALUE.SEPARATORE;
                END IF;
        RETURN ODCICONST.SUCCESS;
    END;

    MEMBER FUNCTION ODCIAGGREGATETERMINATE(SELF IN T_LISTAGG_DISTINCT, RETURN_VALUE OUT VARCHAR2, FLAGS IN NUMBER) RETURN NUMBER IS
      STRINGA_OUTPUT            CLOB:='';
            LISTA_OUTPUT                T_LISTA_ELEMENTI;
            TERMINATORE                 VARCHAR2(3):='...';
            LUNGHEZZA_MAX           NUMBER:=4000;
    BEGIN

                IF SELF.LISTA_ELEMENTI.EXISTS(1) THEN -- se esiste almeno un elemento nella lista

                        -- inizializza una nuova lista di appoggio
                        LISTA_OUTPUT := T_LISTA_ELEMENTI();

                        -- riversamento dei soli elementi in DISTINCT
                        LISTA_OUTPUT := SELF.LISTA_ELEMENTI MULTISET UNION DISTINCT SELF.LISTA_ELEMENTI;

                        -- ordinamento degli elementi
                        SELECT CAST(MULTISET(SELECT * FROM TABLE(LISTA_OUTPUT) ORDER BY 1 ) AS T_LISTA_ELEMENTI ) INTO LISTA_OUTPUT FROM DUAL;

                        -- concatenazione in una stringa                        
                        FOR I IN LISTA_OUTPUT.FIRST .. LISTA_OUTPUT.LAST - 1
                        LOOP
                            STRINGA_OUTPUT := STRINGA_OUTPUT || LISTA_OUTPUT(I) || SELF.SEPARATORE;
                        END LOOP;
                        STRINGA_OUTPUT := STRINGA_OUTPUT || LISTA_OUTPUT(LISTA_OUTPUT.LAST);

                        -- se la stringa supera la dimensione massima impostata, tronca e termina con un terminatore
                        IF LENGTH(STRINGA_OUTPUT) > LUNGHEZZA_MAX THEN
                                    RETURN_VALUE := SUBSTR(STRINGA_OUTPUT, 0, LUNGHEZZA_MAX - LENGTH(TERMINATORE)) || TERMINATORE;
                        ELSE
                                    RETURN_VALUE:=STRINGA_OUTPUT;
                        END IF;

                ELSE -- se non esiste nessun elemento, restituisci NULL

                        RETURN_VALUE := NULL;

                END IF;

        RETURN ODCICONST.SUCCESS;
    END;

    MEMBER FUNCTION ODCIAGGREGATEMERGE(SELF IN OUT T_LISTAGG_DISTINCT, CTX2 IN T_LISTAGG_DISTINCT) RETURN NUMBER IS
    BEGIN
        RETURN ODCICONST.SUCCESS;
    END;

END; -- fine corpo

CREATE
FUNCTION LISTAGG_DISTINCT (INPUT LISTAGG_DISTINCT_PARAMS) RETURN VARCHAR2
    PARALLEL_ENABLE AGGREGATE USING T_LISTAGG_DISTINCT;

// Example
SELECT LISTAGG_DISTINCT(LISTAGG_DISTINCT_PARAMS(OWNER, ', ')) AS LISTA_OWNER
FROM SYS.ALL_OBJECTS;

죄송합니다. 어떤 경우에는 (매우 큰 집합의 경우) Oracle이 다음 오류를 반환 할 수 있습니다.

Object or Collection value was too large. The size of the value
might have exceeded 30k in a SORT context, or the size might be
too big for available memory.

그러나 나는 이것이 좋은 출발점이라고 생각한다;)


0

select col1, listaggr(col2,',') within group(Order by col2) from table group by col1 순서 n을 유지하면서 문자열 (col2)을 목록으로 집계 한 다음 나중에 col1별로 그룹으로 중복을 처리합니다. 즉, col1 중복을 1 그룹에 병합합니다. 아마도 이것은 깨끗하고 단순 해 보일 것이며 col3도 원한다면 listagg ()를 하나 더 추가해야합니다.select col1, listaggr(col2,',') within group(Order by col2),listaggr(col3,',') within group(order by col3) from table group by col1


0

SELECT DISTINCT ...@a_horse_with_no_name에서 언급했듯이 LISTAGG를 호출하기 전에 하위 쿼리의 일부로 사용 하는 것이 아마도 간단한 쿼리에 가장 좋은 방법 일 것입니다.

그러나 더 복잡한 쿼리에서는이를 수행하는 것이 불가능하거나 쉽지 않을 수 있습니다. 나는 이것이 분석 함수를 사용하는 top-n 접근 방식을 사용하는 시나리오에서 나왔습니다.

그래서 COLLECT집계 함수를 찾았습니다 . UNIQUE또는 DISTINCT수정자를 사용할 수 있도록 문서화되어 있습니다. 10g 에서만 조용히 실패합니다 (오류없이 수정자를 무시 함). 그러나 이것을 극복하기 위해 다른 답변 에서이 솔루션에 도달했습니다.

SELECT
  ...
  (
    SELECT LISTAGG(v.column_value,',') WITHIN GROUP (ORDER BY v.column_value)
    FROM TABLE(columns_tab) v
  ) AS columns,
  ...
FROM (
  SELECT
    ...
    SET(CAST(COLLECT(UNIQUE some_column ORDER BY some_column) AS tab_typ)) AS columns_tab,
    ...
)

기본적으로를 사용하여 SET내 컬렉션에서 중복을 제거합니다.

여전히를 tab_typ기본 컬렉션 유형으로 정의해야 하며,의 경우 VARCHAR예를 들면 다음과 같습니다.

CREATE OR REPLACE type tab_typ as table of varchar2(100)
/

또한 여러 열 상황에서 @a_horse_with_no_name의 답변에 대한 수정으로, 세 번째 (또는 그 이상) 열에서 여전히 집계 할 수 있습니다.

select
  col1, 
  listagg(CASE rn2 WHEN 1 THEN col2 END, ',') within group (order by col2) AS col2_list,
  listagg(CASE rn3 WHEN 1 THEN col3 END, ',') within group (order by col3) AS col3_list,
  SUM(col4) AS col4
from (
  select
    col1, 
    col2,
    row_number() over (partition by col1, col2 order by null) as rn2,
    row_number() over (partition by col1, col3 order by null) as rn3
  from foo
)
group by col1;

rn = 1쿼리에 대한 조건을 where 조건으로 두면 다른 열을 잘못 집계하게됩니다.


0

매우 간단합니다. 쿼리에서 고유 한 선택이있는 하위 쿼리를 사용합니다.

SELECT question_id,
       LISTAGG(element_id, ',') WITHIN GROUP (ORDER BY element_id)
FROM
       (SELECT distinct question_id, element_id
       FROM YOUR_TABLE)
GROUP BY question_id;

-1

여러 listagg를 처리하는 가장 간단한 방법은 선택 구별에서 해당 열의 listagg를 포함하는 열당 1 개의 WITH (하위 쿼리 요소)를 사용하는 것입니다.

    WITH tab AS 
    (           
        SELECT 1 as col1, 2 as col2, 3 as col3, 'Smith' as created_by FROM dual
        UNION ALL SELECT 1 as col1, 2 as col2, 3 as col3,'John'  as created_by FROM dual
        UNION ALL SELECT 1 as col1, 3 as col2, 4 as col3,'Ajay'  as created_by FROM dual
        UNION ALL SELECT 1 as col1, 4 as col2, 4 as col3,'Ram'   as created_by FROM dual
        UNION ALL SELECT 1 as col1, 5 as col2, 6 as col3,'Jack'  as created_by FROM dual
    )
    , getCol2 AS
    (
        SELECT  DISTINCT col1, listagg(col2,',') within group (order by col2)  over (partition by col1) AS col2List
        FROM ( SELECT DISTINCT col1,col2 FROM tab)
    )
    , getCol3 AS
    (
        SELECT  DISTINCT col1, listagg(col3,',') within group (order by col3)  over (partition by col1) AS col3List
        FROM ( SELECT DISTINCT col1,col3 FROM tab)
    )
    select col1,col2List,col3List
    FROM getCol2
    JOIN getCol3
    using (col1)

다음을 제공합니다.

col1  col2List  col3List
1     2,3,4,5   3,4,6
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.