Oracle 11g에서 새롭게 소개된 기능 중, LISTAGG Function에 대해 알아보고자 합니다.
우선 테스트 DB의 Oracle Version 확인 및 샘플 데이터를 생성해 보도록 하겠습니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
-- 1. Check Oracle Version SELECT * FROM v$version ; BANNER ---------------------------------------------------------------------------- Oracle Database 11g Enterprise Edition Release 11.2.0.2.0 - 64bit Production PL/SQL Release 11.2.0.2.0 - Production ; -- 2. Create Table DROP TABLE EMP; CREATE TABLE EMP(DEPTNO NUMBER, ENAME VARCHAR2(1000), HIREDATE DATE); -- 3. Data Insert INSERT INTO EMP(DEPTNO, ENAME, HIREDATE) VALUES(10, 'CLARK ', TO_DATE('19810609', 'YYYYMMDD')); INSERT INTO EMP(DEPTNO, ENAME, HIREDATE) VALUES(10, 'KING', TO_DATE('19811117', 'YYYYMMDD')); INSERT INTO EMP(DEPTNO, ENAME, HIREDATE) VALUES(10, 'MILLER', TO_DATE('19820123', 'YYYYMMDD')); INSERT INTO EMP(DEPTNO, ENAME, HIREDATE) VALUES(20, 'ADAMS', TO_DATE('19830112', 'YYYYMMDD')); INSERT INTO EMP(DEPTNO, ENAME, HIREDATE) VALUES(20, 'FORD', TO_DATE('19811203', 'YYYYMMDD')); INSERT INTO EMP(DEPTNO, ENAME, HIREDATE) VALUES(20, 'JONES', TO_DATE('19810402', 'YYYYMMDD')); INSERT INTO EMP(DEPTNO, ENAME, HIREDATE) VALUES(20, 'SCOTT', TO_DATE('19821209', 'YYYYMMDD')); INSERT INTO EMP(DEPTNO, ENAME, HIREDATE) VALUES(20, 'SMITH', TO_DATE('19801217', 'YYYYMMDD')); INSERT INTO EMP(DEPTNO, ENAME, HIREDATE) VALUES(30, 'ALLEN', TO_DATE('19810220', 'YYYYMMDD')); INSERT INTO EMP(DEPTNO, ENAME, HIREDATE) VALUES(30, 'BLAKE', TO_DATE('19810501', 'YYYYMMDD')); INSERT INTO EMP(DEPTNO, ENAME, HIREDATE) VALUES(30, 'JAMES', TO_DATE('19811203', 'YYYYMMDD')); INSERT INTO EMP(DEPTNO, ENAME, HIREDATE) VALUES(30, 'MARTIN', TO_DATE('19810928', 'YYYYMMDD')); INSERT INTO EMP(DEPTNO, ENAME, HIREDATE) VALUES(30, 'TURNER', TO_DATE('19810908', 'YYYYMMDD')); INSERT INTO EMP(DEPTNO, ENAME, HIREDATE) VALUES(30, 'WARD', TO_DATE('19810222', 'YYYYMMDD')); COMMIT; |
이렇게 생성된 데이터를 확인해 보겠습니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
-- 4. Query EMP col deptno format 999,999,999 col ename format a10 col hiredate format a20 SELECT DEPTNO, ENAME, TO_CHAR(HIREDATE, 'YYYY-MM-DD') HIREDATE FROM EMP ; DEPTNO ENAME HIREDATE ------------ ---------- ------------ 10 CLARK 1981-06-09 10 KING 1981-11-17 10 MILLER 1982-01-23 20 ADAMS 1983-01-12 20 FORD 1981-12-03 20 JONES 1981-04-02 20 SCOTT 1982-12-09 20 SMITH 1980-12-17 30 ALLEN 1981-02-20 30 BLAKE 1981-05-01 30 JAMES 1981-12-03 30 MARTIN 1981-09-28 30 TURNER 1981-09-08 30 WARD 1981-02-22 |
이제 이 데이터를 바탕으로, DEPTNO를 GROUP으로 묶은 후 ENAME 값을 가로로 나열하고자 하는데, 10g에서는 이를 구현하기 위해, CONNECT BY 구문을 사용해야 했습니다.
아래의 예제를 살펴보겠습니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
-- 5. Query aggregated ename in 10g version col deptno format 999,999,999 col aggregated_enames format a50 SELECT DEPTNO, SUBSTR(MAX(SYS_CONNECT_BY_PATH(ENAME, ', ')), 2) AGGREGATED_ENAMES FROM (SELECT DEPTNO, ENAME, TO_CHAR(HIREDATE, 'YYYY-MM-DD') HIREDATE, ROW_NUMBER() OVER(PARTITION BY DEPTNO ORDER BY HIREDATE) CNT FROM EMP) T CONNECT BY PRIOR CNT = CNT - 1 AND PRIOR DEPTNO = DEPTNO START WITH CNT = 1 GROUP BY DEPTNO ORDER BY 1 ; DEPTNO AGGREGATED_ENAMES ------------ ---------------------------------------------- 10 CLARK, KING, MILLER 20 SMITH, JONES, FORD, SCOTT, ADAMS 30 ALLEN, WARD, BLAKE, TURNER, MARTIN, JAMES |
즉 DEPTNO 값을 GROUPING 하기 위해 ROWNUM() 함수를 사용한 후, CONNECT BY PRIOR를 이용하여 연결한 뒤, SYS_CONNECT_BY_PATH 함수를 사용하여 값을 가로로 나열하였습니다.
하지만, 11g에서는 이를 구현하기 위해서 복잡하게 SQL을 구현할 필요가 없이 LISTAGG 함수를 사용하면 됩니다.
1 2 3 4 5 6 7 |
Syntax : LISTAGG( [,]) WITHIN GROUP (ORDER BY ) [OVER (PARTITION BY )] The following elements are mandatory: 1) the column or expression to be aggregated; 2) the WITHIN GROUP keywords; 3) the ORDER BY clause within the grouping |
이제 LISTAGG Function이 사용된 여러 Sample을 통해 어떻게 활용이 가능한지 살펴보겠습니다.
1 2 3 4 5 6 7 8 9 10 11 |
SELECT DEPTNO, LISTAGG(ENAME, ', ') WITHIN GROUP (ORDER BY HIREDATE) AS AGGREGATED_ENAMES FROM EMP GROUP BY DEPTNO ; DEPTNO AGGREGATED_ENAMES ------------ ---------------------------------------------- 10 CLARK, KING, MILLER 20 SMITH, JONES, FORD, SCOTT, ADAMS 30 ALLEN, WARD, BLAKE, TURNER, MARTIN, JAMES |
사용방법은 간단합니다. GROUPING 하고자 하는 컬럼을 GROUP BY로 묶은 후, 가로로 나열하고자 하는 컬럼을 LASTAGG()에 명시하면 됩니다. 이후, WITHIN GROUP() 함수에서 가로로 나열하고자 하는 순서를 ORDER BY로 지정하면 끝입니다.
그럼 항상 GROUP BY 함수를 사용해야 하나? 10g의 분석함수처럼 여러 컬럼 데이터와 동시에 볼 수는 없을까?
이와 같은 의문에 대해, 아래의 예제에서 확인해 보겠습니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
SELECT DEPTNO, ENAME, HIREDATE, LISTAGG(ENAME, ', ') WITHIN GROUP (ORDER BY HIREDATE) OVER(PARTITION BY DEPTNO) AS AGGREGATED_ENAMES FROM EMP ; DEPTNO ENAME HIREDATE AGGREGATED_ENAMES ------------ ---------- -------------------- ------------------------------------------- 10 CLARK 09-JUN-81 CLARK, KING, MILLER 10 KING 17-NOV-81 CLARK, KING, MILLER 10 MILLER 23-JAN-82 CLARK, KING, MILLER 20 SMITH 17-DEC-80 SMITH, JONES, FORD, SCOTT, ADAMS 20 JONES 02-APR-81 SMITH, JONES, FORD, SCOTT, ADAMS 20 FORD 03-DEC-81 SMITH, JONES, FORD, SCOTT, ADAMS 20 SCOTT 09-DEC-82 SMITH, JONES, FORD, SCOTT, ADAMS 20 ADAMS 12-JAN-83 SMITH, JONES, FORD, SCOTT, ADAMS 30 ALLEN 20-FEB-81 ALLEN, WARD, BLAKE, TURNER, MARTIN, JAMES 30 WARD 22-FEB-81 ALLEN, WARD, BLAKE, TURNER, MARTIN, JAMES 30 BLAKE 01-MAY-81 ALLEN, WARD, BLAKE, TURNER, MARTIN, JAMES 30 TURNER 08-SEP-81 ALLEN, WARD, BLAKE, TURNER, MARTIN, JAMES 30 MARTIN 28-SEP-81 ALLEN, WARD, BLAKE, TURNER, MARTIN, JAMES 30 JAMES 03-DEC-81 ALLEN, WARD, BLAKE, TURNER, MARTIN, JAMES |
Sample #1에서 사용한 LISTAGG 구문에 OVER(PARTITION BY DEPTNO) 구문을 추가할 경우, 분석함수처럼 기존 컬럼들과 같이 볼 수 있습니다.
1 2 3 4 5 6 7 8 9 10 11 |
SELECT DEPTNO, LISTAGG(ENAME) WITHIN GROUP (ORDER BY HIREDATE) AS AGGREGATED_ENAMES FROM EMP GROUP BY DEPTNO ; DEPTNO AGGREGATED_ENAMES ------------ -------------------------------- 10 CLARKKINGMILLER 20 SMITHJONESFORDSCOTTADAMS 30 ALLENWARDBLAKETURNERMARTINJAMES |
이번 예제는 LISTAGG 함수의 2번째 파라마터 값을(구분자) 아무것도 안 줄 경우, 모든 값이 연속으로 연결되는 것을 확인할 수 있습니다.
이번에는 이 함수 사용 시 제약사항에 대해 살펴보도록 하겠습니다.
1 2 3 4 5 6 7 8 9 10 |
SELECT DEPTNO, LISTAGG(ENAME, ', ') WITHIN GROUP () AS AGGREGATED_ENAMES FROM EMP GROUP BY DEPTNO ; LISTAGG(ENAME, ', ') WITHIN GROUP () AS AGGREGATED_ENAMES * ERROR at line 2: ORA-30491: missing ORDER BY clause |
당연한 이야기지만, WITHIN GROUP 함수 파라미터에 값을 주지 않을 경우 에러가 납니다.
1 2 3 4 5 6 7 8 9 10 |
SELECT DEPTNO, LISTAGG(ENAME, '(' || ROWNUM || ')') WITHIN GROUP (ORDER BY HIREDATE) AS AGGREGATED_ENAMES FROM EMP GROUP BY DEPTNO ; LISTAGG(ENAME, '(' || ROWNUM || ')') WITHIN GROUP (ORDER BY HIREDATE) AS AGGREGATED_ENAMES * ERROR at line 2: ORA-30497: Argument should be a constant or a function of expressions in GROUP BY. |
이번에는 LISTAGG 함수의 2번째 파라미터에 ROWNUM 같은 예약어는 사용 불가함을 알 수 있습니다.
1 2 3 4 5 6 7 8 9 10 11 |
SELECT DEPTNO, LISTAGG(ENAME, '(' || CHR(DEPTNO+55) || ')') WITHIN GROUP (ORDER BY HIREDATE) AS AGGREGATED_ENAMES FROM EMP GROUP BY DEPTNO ; DEPTNO AGGREGATED_ENAMES ------------ ---------------------------------------------- 10 CLARK(A)KING(A)MILLER 20 SMITH(K)JONES(K)FORD(K)SCOTT(K)ADAMS 30 ALLEN(U)WARD(U)BLAKE(U)TURNER(U)MARTIN(U)JAMES |
이번에는 LISTAGG 함수의 2번째 파라미터에 예약어가 아닌 CHR() 함수를 사용할 경우, 에러 없이 출력이 가능한 것을 볼 수 있습니다.
1 2 3 4 5 6 7 8 |
SELECT LISTAGG(OBJECT_NAME) WITHIN GROUP (ORDER BY NULL) FROM ALL_OBJECTS ; FROM ALL_OBJECTS * ERROR at line 2: ORA-01489: result of string concatenation is too long |
이번 예제는 LISTAGG 함수를 사용하여 String 값을 가져올 때 너무 길이가 긴 값은 가져올 수 없음을 확인할 수 있는 예제입니다. 여기서는 ALL_OBJECTS에서 모든 레코드의 OBJECT_NAME 값을 연결시키고자 했으며, 그 길이가 너무 길어 에러가 났음을 알 수 있습니다.
이상으로 11g에서 새롭게 소개된 LISTAGG 함수에 대해 알아보았습니다.
다음 포스팅 글에서도 11g의 유용한 기능에 대해 소개하고자 하며, 이번 시간에는 여기서 글을 마치도록 하겠습니다.
reference site : http://www.oracle-developer.net/display.php?id=515
About the Author
● 경력 10년(현 LG전자 GERP DBA/Tuning 업무)
● 세미나 : 오라클 SQL 튜닝 방법의 이해, DBMS_XPLAN과 10046 Trace를 이용한 SQL문 튜닝, 개발자를 위한 어플리케이션튜닝 세미나
Related Posts
- [DB 칼럼] TRIGGER 사용방법 15-02-05 일반태그: Application Architecture. DB태그: Oracle.
- [DB 칼럼] Pivot queries (11g New Feature) 14-09-15 일반태그: Application Architecture. DB태그: Oracle.
댓글작성시 Code-Highlighter 삽입방법
Syntax [code title="..." theme="..."]coding...[/code]
Example [plsql title="현재시간 출력문" theme="classic"] select now() [/plsql]
Code-List plsql, mysql, java, objc, js, c#, c++, delphi, apache, php, css ...
Theme-List ado, arduino-ide, bncplusplus, cg-cookie, cisco-router, classic, eclipse, epicgeeks, familiar, feeldesign, github, idle, inlellij-idea, iris-vfx, mirc-dark, monokai, neon, secrets-of-rock, solarized-dark, solarized-light, son-of-obsidian, ssms2012, terminal, tomorrow, tomorrow-night, turnwall, twilight, vs2012 View Theme Demo
Alert 댓글에서 직접 코드 작성시 줄바꿈은 (Enter)값 대신 (Shift+Enter) 사용할 것