{"id":207,"date":"2012-12-23T00:21:10","date_gmt":"2012-12-22T23:21:10","guid":{"rendered":"http:\/\/www.salvis.com\/blog\/?p=207"},"modified":"2023-11-08T19:59:11","modified_gmt":"2023-11-08T18:59:11","slug":"building-comma-separated-values-with-oracle-sql","status":"publish","type":"post","link":"https:\/\/www.salvis.com\/blog\/2012\/12\/23\/building-comma-separated-values-with-oracle-sql\/","title":{"rendered":"Building Comma Separated Values with Oracle &#038; SQL"},"content":{"rendered":"\n<p>From time to time I&#8217;m asked to aggregate strings from multiple records into a single column using SQL. Here&#8217;s an example, showing a comma-separated list of ordered employee names per department based on the famous EMP and DEPT tables.<\/p>\n\n\n\n<table id=\"tablepress-2\" class=\"tablepress tablepress-id-2\">\n<thead>\n<tr class=\"row-1\">\n\t<th class=\"column-1\">DEPTNO<\/th><th class=\"column-2\">DNAME<\/th><th class=\"column-3\">ENAME_LIST<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"row-striping\">\n<tr class=\"row-2\">\n\t<td class=\"column-1\">10<\/td><td class=\"column-2\">ACCOUNTING<\/td><td class=\"column-3\">CLARK, KING, MILLER<\/td>\n<\/tr>\n<tr class=\"row-3\">\n\t<td class=\"column-1\">20<\/td><td class=\"column-2\">RESEARCH<\/td><td class=\"column-3\">ADAMS, FORD, JONES, SCOTT, SMITH<\/td>\n<\/tr>\n<tr class=\"row-4\">\n\t<td class=\"column-1\">30<\/td><td class=\"column-2\">SALES<\/td><td class=\"column-3\">ALLEN, BLAKE, JAMES, MARTIN, TURNER, WARD<\/td>\n<\/tr>\n<tr class=\"row-5\">\n\t<td class=\"column-1\">40<\/td><td class=\"column-2\">OPERATIONS<\/td><td class=\"column-3\"><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n\n\n\n<p>Oracle introduced the aggregate function LISTAGG for that purpose in 11.2. If you may use LISTAGG go for it, but if you have to work with an older version of the Oracle Database Server you might be interested in some other options which I discuss per Oracle Database version.<\/p>\n\n\n\n<p>I cover just some options in this post, if you are interested in more then please visit Tim Hall&#8217;s <a title=\"String Aggregation Techniques\" href=\"http:\/\/www.oracle-base.com\/articles\/misc\/string-aggregation-techniques.php\" target=\"_blank\" rel=\"noopener noreferrer\">String Aggregation Techniques<\/a> on <a href=\"http:\/\/www.oracle-base.com\" target=\"_blank\" rel=\"noopener noreferrer\">oracle-base.com<\/a>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Oracle7<\/h3>\n\n\n\n<p>More than twenty years ago PL\/SQL was introduced as part of the Oracle Database Server version 7.0 allowing to write functions to be used in SQL statements. Back then something like the following was necessary to build comma-separated values:<br \/><a name=\"1\"><\/a><\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#2b2b2b;color:#c7c7c7\">1) Oracle7 UDF Style<\/span><span role=\"button\" tabindex=\"0\" data-code=\"CREATE OR REPLACE FUNCTION deptno_to_ename_list(in_deptno IN VARCHAR2)\n   RETURN VARCHAR2 IS\n   CURSOR l_cur IS\n      SELECT ename\n        FROM emp\n       WHERE deptno = in_deptno\n       ORDER BY ename;\n   l_ret VARCHAR2(2000);\nBEGIN\n   FOR l_rec IN l_cur\n   LOOP\n      IF l_cur%ROWCOUNT &gt; 1 THEN\n         l_ret := l_ret || ', ';\n      END IF;\n      l_ret := l_ret || l_rec.ename;\n   END LOOP;\n   RETURN l_ret;\nEND;\n\/\n\nSELECT deptno, dname, deptno_to_ename_list(deptno) AS ename_list\n  FROM dept\n ORDER BY deptno;\" style=\"color:#D4D4D4;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki dark-plus\" style=\"background-color: #1E1E1E\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #569CD6\">CREATE OR REPLACE<\/span><span style=\"color: #D4D4D4\"> FUNCTION deptno_to_ename_list(in_deptno <\/span><span style=\"color: #569CD6\">IN<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">VARCHAR2<\/span><span style=\"color: #D4D4D4\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">   <\/span><span style=\"color: #C586C0\">RETURN<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">VARCHAR2<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">IS<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">   <\/span><span style=\"color: #569CD6\">CURSOR<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">l_cur<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">IS<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">      <\/span><span style=\"color: #569CD6\">SELECT<\/span><span style=\"color: #D4D4D4\"> ename<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">        <\/span><span style=\"color: #569CD6\">FROM<\/span><span style=\"color: #D4D4D4\"> emp<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">       <\/span><span style=\"color: #569CD6\">WHERE<\/span><span style=\"color: #D4D4D4\"> deptno = in_deptno<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">       <\/span><span style=\"color: #569CD6\">ORDER BY<\/span><span style=\"color: #D4D4D4\"> ename;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">   <\/span><span style=\"color: #9CDCFE\">l_ret<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">VARCHAR2<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #B5CEA8\">2000<\/span><span style=\"color: #D4D4D4\">);<\/span><\/span>\n<span class=\"line\"><span style=\"color: #569CD6\">BEGIN<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">   <\/span><span style=\"color: #C586C0\">FOR<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">l_rec<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">IN<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">l_cur<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">   <\/span><span style=\"color: #C586C0\">LOOP<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">      <\/span><span style=\"color: #C586C0\">IF<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">l_cur<\/span><span style=\"color: #DCDCAA\">%ROWCOUNT<\/span><span style=\"color: #D4D4D4\"> &gt; <\/span><span style=\"color: #B5CEA8\">1<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">THEN<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">         <\/span><span style=\"color: #9CDCFE\">l_ret<\/span><span style=\"color: #D4D4D4\"> := <\/span><span style=\"color: #9CDCFE\">l_ret<\/span><span style=\"color: #D4D4D4\"> || <\/span><span style=\"color: #CE9178\">&#39;, &#39;<\/span><span style=\"color: #D4D4D4\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">      <\/span><span style=\"color: #C586C0\">END IF<\/span><span style=\"color: #D4D4D4\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">      <\/span><span style=\"color: #9CDCFE\">l_ret<\/span><span style=\"color: #D4D4D4\"> := <\/span><span style=\"color: #9CDCFE\">l_ret<\/span><span style=\"color: #D4D4D4\"> || <\/span><span style=\"color: #9CDCFE\">l_rec<\/span><span style=\"color: #D4D4D4\">.ename;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">   <\/span><span style=\"color: #C586C0\">END LOOP<\/span><span style=\"color: #D4D4D4\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">   <\/span><span style=\"color: #C586C0\">RETURN<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">l_ret<\/span><span style=\"color: #D4D4D4\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #569CD6\">END<\/span><span style=\"color: #D4D4D4\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">\/<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #569CD6\">SELECT<\/span><span style=\"color: #D4D4D4\"> deptno, dname, deptno_to_ename_list(deptno) <\/span><span style=\"color: #569CD6\">AS<\/span><span style=\"color: #D4D4D4\"> ename_list<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">  <\/span><span style=\"color: #569CD6\">FROM<\/span><span style=\"color: #D4D4D4\"> dept<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">ORDER BY<\/span><span style=\"color: #D4D4D4\"> deptno;<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<h3 class=\"wp-block-heading\">Oracle8<\/h3>\n\n\n\n<p>Version 8.0 came with the object option allowing to solve the problem in more generic ways.<br \/><a name=\"2\"><\/a><\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#2b2b2b;color:#c7c7c7\">2) Oracle8 Collection Type Style<\/span><span role=\"button\" tabindex=\"0\" data-code=\"CREATE OR REPLACE TYPE string_tabtype IS TABLE OF VARCHAR2(2000);\n\nCREATE OR REPLACE FUNCTION collection_to_comma_list(\n   in_strings IN string_tabtype\n) RETURN VARCHAR2 IS\n   l_ret VARCHAR2(2000);\nBEGIN\n   IF in_strings.COUNT &gt; 0 THEN\n      FOR i IN 1 .. in_strings.COUNT\n      LOOP\n         IF i &gt; 1 THEN\n            l_ret := l_ret || ', ';\n         END IF;\n         l_ret := l_ret || in_strings(i);\n      END LOOP;\n   END IF;\n   RETURN l_ret;\nEND;\n\/\n\nSELECT d.deptno,\n       d.dname,\n       collection_to_comma_list(\n          CAST(\n             MULTISET(\n                SELECT ename\n                  FROM emp e\n                 WHERE e.deptno = d.deptno\n                 ORDER BY ename\n             ) AS string_tabtype\n          )\n       ) AS ename_list\n  FROM dept d\n ORDER BY d.deptno;\" style=\"color:#D4D4D4;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki dark-plus\" style=\"background-color: #1E1E1E\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #569CD6\">CREATE OR REPLACE<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">TYPE<\/span><span style=\"color: #D4D4D4\"> string_tabtype <\/span><span style=\"color: #569CD6\">IS<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">TABLE<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">OF<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">VARCHAR2<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #B5CEA8\">2000<\/span><span style=\"color: #D4D4D4\">);<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #569CD6\">CREATE OR REPLACE<\/span><span style=\"color: #D4D4D4\"> FUNCTION collection_to_comma_list(<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">   in_strings <\/span><span style=\"color: #569CD6\">IN<\/span><span style=\"color: #D4D4D4\"> string_tabtype<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">) <\/span><span style=\"color: #C586C0\">RETURN<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">VARCHAR2<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">IS<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">   <\/span><span style=\"color: #9CDCFE\">l_ret<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">VARCHAR2<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #B5CEA8\">2000<\/span><span style=\"color: #D4D4D4\">);<\/span><\/span>\n<span class=\"line\"><span style=\"color: #569CD6\">BEGIN<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">   <\/span><span style=\"color: #C586C0\">IF<\/span><span style=\"color: #D4D4D4\"> in_strings<\/span><span style=\"color: #DCDCAA\">.COUNT<\/span><span style=\"color: #D4D4D4\"> &gt; <\/span><span style=\"color: #B5CEA8\">0<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">THEN<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">      <\/span><span style=\"color: #C586C0\">FOR<\/span><span style=\"color: #D4D4D4\"> i <\/span><span style=\"color: #569CD6\">IN<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #B5CEA8\">1<\/span><span style=\"color: #D4D4D4\"> .. in_strings<\/span><span style=\"color: #DCDCAA\">.COUNT<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">      <\/span><span style=\"color: #C586C0\">LOOP<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">         <\/span><span style=\"color: #C586C0\">IF<\/span><span style=\"color: #D4D4D4\"> i &gt; <\/span><span style=\"color: #B5CEA8\">1<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">THEN<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">            <\/span><span style=\"color: #9CDCFE\">l_ret<\/span><span style=\"color: #D4D4D4\"> := <\/span><span style=\"color: #9CDCFE\">l_ret<\/span><span style=\"color: #D4D4D4\"> || <\/span><span style=\"color: #CE9178\">&#39;, &#39;<\/span><span style=\"color: #D4D4D4\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">         <\/span><span style=\"color: #C586C0\">END IF<\/span><span style=\"color: #D4D4D4\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">         <\/span><span style=\"color: #9CDCFE\">l_ret<\/span><span style=\"color: #D4D4D4\"> := <\/span><span style=\"color: #9CDCFE\">l_ret<\/span><span style=\"color: #D4D4D4\"> || in_strings(i);<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">      <\/span><span style=\"color: #C586C0\">END LOOP<\/span><span style=\"color: #D4D4D4\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">   <\/span><span style=\"color: #C586C0\">END IF<\/span><span style=\"color: #D4D4D4\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">   <\/span><span style=\"color: #C586C0\">RETURN<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">l_ret<\/span><span style=\"color: #D4D4D4\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #569CD6\">END<\/span><span style=\"color: #D4D4D4\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">\/<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #569CD6\">SELECT<\/span><span style=\"color: #D4D4D4\"> d.deptno,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">       d.dname,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">       collection_to_comma_list(<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">          <\/span><span style=\"color: #DCDCAA\">CAST<\/span><span style=\"color: #D4D4D4\">(<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">             MULTISET(<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">                <\/span><span style=\"color: #569CD6\">SELECT<\/span><span style=\"color: #D4D4D4\"> ename<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">                  <\/span><span style=\"color: #569CD6\">FROM<\/span><span style=\"color: #D4D4D4\"> emp e<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">                 <\/span><span style=\"color: #569CD6\">WHERE<\/span><span style=\"color: #D4D4D4\"> e.deptno = d.deptno<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">                 <\/span><span style=\"color: #569CD6\">ORDER BY<\/span><span style=\"color: #D4D4D4\"> ename<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">             ) <\/span><span style=\"color: #569CD6\">AS<\/span><span style=\"color: #D4D4D4\"> string_tabtype<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">          )<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">       ) <\/span><span style=\"color: #569CD6\">AS<\/span><span style=\"color: #D4D4D4\"> ename_list<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">  <\/span><span style=\"color: #569CD6\">FROM<\/span><span style=\"color: #D4D4D4\"> dept d<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">ORDER BY<\/span><span style=\"color: #D4D4D4\"> d.deptno;<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p>Another option was to use a REF CURSOR instead of a collection type. The PL\/SQL part was executable in Oracle7 too, but the CURSOR expression was not available back then.&nbsp;BTW: SYS_REFCURSOR was introduced in 9.0, so this specific PL\/SQL type is really necessary with version 8.0.<br \/><a name=\"3\"><\/a><\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#2b2b2b;color:#c7c7c7\">3) Oracle8 RefCursor Style<\/span><span role=\"button\" tabindex=\"0\" data-code=\"CREATE OR REPLACE PACKAGE mytypes_pkg IS\n   TYPE refcursor_type IS REF CURSOR; \nEND mytypes_pkg;\n\/ \n\nCREATE OR REPLACE FUNCTION cursor_to_comma_list(\n   in_refcursor IN mytypes_pkg.refcursor_type\n) RETURN VARCHAR2 IS\n   l_string VARCHAR2(2000);\n   l_ret VARCHAR2(2000);\nBEGIN\n   LOOP\n      FETCH in_refcursor INTO l_string;\n      EXIT WHEN in_refcursor%NOTFOUND;\n      IF in_refcursor%ROWCOUNT &gt; 1 THEN\n         l_ret := l_ret || ', ';\n      END IF;\n      l_ret := l_ret || l_string;\n   END LOOP;\n   CLOSE in_refcursor;\n   RETURN l_ret;\nEND;\n\/\n\nSELECT d.deptno,\n       d.dname,\n       cursor_to_comma_list(\n          CURSOR(\n             SELECT ename\n               FROM emp e\n              WHERE e.deptno = d.deptno\n              ORDER BY ename\n          ) \n       ) AS ename_list\n  FROM dept d\n ORDER BY d.deptno;\" style=\"color:#D4D4D4;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki dark-plus\" style=\"background-color: #1E1E1E\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #569CD6\">CREATE OR REPLACE<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">PACKAGE<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #4EC9B0\">mytypes_pkg<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">IS<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">   <\/span><span style=\"color: #569CD6\">TYPE<\/span><span style=\"color: #D4D4D4\"> refcursor_type <\/span><span style=\"color: #569CD6\">IS<\/span><span style=\"color: #D4D4D4\"> REF <\/span><span style=\"color: #569CD6\">CURSOR<\/span><span style=\"color: #D4D4D4\">; <\/span><\/span>\n<span class=\"line\"><span style=\"color: #569CD6\">END<\/span><span style=\"color: #D4D4D4\"> mytypes_pkg;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">\/ <\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #569CD6\">CREATE OR REPLACE<\/span><span style=\"color: #D4D4D4\"> FUNCTION cursor_to_comma_list(<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">   in_refcursor <\/span><span style=\"color: #569CD6\">IN<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #DCDCAA\">mytypes_pkg.<\/span><span style=\"color: #4EC9B0\">refcursor_type<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">) <\/span><span style=\"color: #C586C0\">RETURN<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">VARCHAR2<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">IS<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">   <\/span><span style=\"color: #9CDCFE\">l_string<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">VARCHAR2<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #B5CEA8\">2000<\/span><span style=\"color: #D4D4D4\">);<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">   <\/span><span style=\"color: #9CDCFE\">l_ret<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">VARCHAR2<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #B5CEA8\">2000<\/span><span style=\"color: #D4D4D4\">);<\/span><\/span>\n<span class=\"line\"><span style=\"color: #569CD6\">BEGIN<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">   <\/span><span style=\"color: #C586C0\">LOOP<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">      <\/span><span style=\"color: #569CD6\">FETCH<\/span><span style=\"color: #D4D4D4\"> in_refcursor <\/span><span style=\"color: #569CD6\">INTO<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">l_string<\/span><span style=\"color: #D4D4D4\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">      <\/span><span style=\"color: #569CD6\">EXIT<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">WHEN<\/span><span style=\"color: #D4D4D4\"> in_refcursor<\/span><span style=\"color: #DCDCAA\">%NOTFOUND<\/span><span style=\"color: #D4D4D4\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">      <\/span><span style=\"color: #C586C0\">IF<\/span><span style=\"color: #D4D4D4\"> in_refcursor<\/span><span style=\"color: #DCDCAA\">%ROWCOUNT<\/span><span style=\"color: #D4D4D4\"> &gt; <\/span><span style=\"color: #B5CEA8\">1<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">THEN<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">         <\/span><span style=\"color: #9CDCFE\">l_ret<\/span><span style=\"color: #D4D4D4\"> := <\/span><span style=\"color: #9CDCFE\">l_ret<\/span><span style=\"color: #D4D4D4\"> || <\/span><span style=\"color: #CE9178\">&#39;, &#39;<\/span><span style=\"color: #D4D4D4\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">      <\/span><span style=\"color: #C586C0\">END IF<\/span><span style=\"color: #D4D4D4\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">      <\/span><span style=\"color: #9CDCFE\">l_ret<\/span><span style=\"color: #D4D4D4\"> := <\/span><span style=\"color: #9CDCFE\">l_ret<\/span><span style=\"color: #D4D4D4\"> || <\/span><span style=\"color: #9CDCFE\">l_string<\/span><span style=\"color: #D4D4D4\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">   <\/span><span style=\"color: #C586C0\">END LOOP<\/span><span style=\"color: #D4D4D4\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">   <\/span><span style=\"color: #569CD6\">CLOSE<\/span><span style=\"color: #D4D4D4\"> in_refcursor;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">   <\/span><span style=\"color: #C586C0\">RETURN<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">l_ret<\/span><span style=\"color: #D4D4D4\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #569CD6\">END<\/span><span style=\"color: #D4D4D4\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">\/<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #569CD6\">SELECT<\/span><span style=\"color: #D4D4D4\"> d.deptno,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">       d.dname,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">       cursor_to_comma_list(<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">          <\/span><span style=\"color: #569CD6\">CURSOR<\/span><span style=\"color: #D4D4D4\">(<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">             <\/span><span style=\"color: #569CD6\">SELECT<\/span><span style=\"color: #D4D4D4\"> ename<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">               <\/span><span style=\"color: #569CD6\">FROM<\/span><span style=\"color: #D4D4D4\"> emp e<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">              <\/span><span style=\"color: #569CD6\">WHERE<\/span><span style=\"color: #D4D4D4\"> e.deptno = d.deptno<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">              <\/span><span style=\"color: #569CD6\">ORDER BY<\/span><span style=\"color: #D4D4D4\"> ename<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">          ) <\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">       ) <\/span><span style=\"color: #569CD6\">AS<\/span><span style=\"color: #D4D4D4\"> ename_list<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">  <\/span><span style=\"color: #569CD6\">FROM<\/span><span style=\"color: #D4D4D4\"> dept d<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">ORDER BY<\/span><span style=\"color: #D4D4D4\"> d.deptno;<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<h3 class=\"wp-block-heading\">Oracle9<em>i<\/em> Release 1<\/h3>\n\n\n\n<p>Version 9.0 came with basic XML support which allowed to aggregate strings without the need for a helper function.<br \/><a name=\"4\"><\/a><\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#2b2b2b;color:#c7c7c7\">4) Oracle9iR1 SYS_XMLAGG\/String Style<\/span><span role=\"button\" tabindex=\"0\" data-code=\"SELECT d.deptno,\n       d.dname,\n       RTRIM(\n          SYS_XMLAGG(\n             SYS_XMLGEN(\n                e.ename||', '\n             )\n          ).EXTRACT(\n             '\/ROWSET\/ROW\/text()'\n          ).getStringVal(),\n          ', '\n       ) AS ename_list\n  FROM dept d\n  LEFT JOIN emp e ON e.deptno = d.deptno\n GROUP BY d.deptno, d.dname\n ORDER BY d.deptno, d.dname;\" style=\"color:#D4D4D4;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki dark-plus\" style=\"background-color: #1E1E1E\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #569CD6\">SELECT<\/span><span style=\"color: #D4D4D4\"> d.deptno,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">       d.dname,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">       <\/span><span style=\"color: #DCDCAA\">RTRIM<\/span><span style=\"color: #D4D4D4\">(<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">          SYS_XMLAGG(<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">             SYS_XMLGEN(<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">                e.ename||<\/span><span style=\"color: #CE9178\">&#39;, &#39;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">             )<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">          ).EXTRACT(<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">             <\/span><span style=\"color: #CE9178\">&#39;\/ROWSET\/ROW\/text()&#39;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">          ).getStringVal(),<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">          <\/span><span style=\"color: #CE9178\">&#39;, &#39;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">       ) <\/span><span style=\"color: #569CD6\">AS<\/span><span style=\"color: #D4D4D4\"> ename_list<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">  <\/span><span style=\"color: #569CD6\">FROM<\/span><span style=\"color: #D4D4D4\"> dept d<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">  <\/span><span style=\"color: #569CD6\">LEFT JOIN<\/span><span style=\"color: #D4D4D4\"> emp e <\/span><span style=\"color: #569CD6\">ON<\/span><span style=\"color: #D4D4D4\"> e.deptno = d.deptno<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">GROUP BY<\/span><span style=\"color: #D4D4D4\"> d.deptno, d.dname<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">ORDER BY<\/span><span style=\"color: #D4D4D4\"> d.deptno, d.dname;<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p>In this solution, the sort order of the aggregated strings is not definable. Version 9i introduced also user-defined aggregate functions. To use them you need to implement the ODCIAggregate interface which allows you to sort the result.<br \/><a name=\"5\"><\/a><\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#2b2b2b;color:#c7c7c7\">5) Oracle9iR1 Aggregate Function Style<\/span><span role=\"button\" tabindex=\"0\" data-code=\"CREATE OR REPLACE TYPE string_tabtype AS TABLE OF VARCHAR2(2000);\n\nCREATE OR REPLACE TYPE mylistagg_type AS OBJECT (\n  strings string_tabtype,\n  STATIC FUNCTION ODCIAggregateInitialize(\n     sctx        IN OUT mylistagg_type\n  ) RETURN NUMBER,\n  MEMBER FUNCTION ODCIAggregateIterate(\n     SELF        IN OUT mylistagg_type,\n     value       IN     VARCHAR2 \n  ) RETURN NUMBER,\n  MEMBER FUNCTION ODCIAggregateTerminate(\n     SELF        IN     mylistagg_type,\n     returnValue OUT    VARCHAR2,\n     flags       IN     NUMBER\n  ) RETURN NUMBER,\n  MEMBER FUNCTION ODCIAggregateMerge(\n     SELF        IN OUT  mylistagg_type,\n     ctx2        IN      mylistagg_type)\n    RETURN NUMBER\n);\n\nCREATE OR REPLACE TYPE BODY mylistagg_type IS\n  STATIC FUNCTION ODCIAggregateInitialize(\n     sctx        IN OUT mylistagg_type\n  ) RETURN NUMBER IS\n  BEGIN\n    sctx := mylistagg_type(string_tabtype());\n    RETURN ODCIConst.Success;\n  END;\n\n  MEMBER FUNCTION ODCIAggregateIterate(\n     SELF        IN OUT mylistagg_type,\n     value       IN     VARCHAR2 \n  ) RETURN NUMBER IS\n  BEGIN\n    SELF.strings.EXTEND;\n    SELF.strings(SELF.strings.COUNT) := VALUE;\n    RETURN ODCIConst.Success;\n  END ODCIAggregateIterate;\n\n  MEMBER FUNCTION ODCIAggregateTerminate(\n     SELF        IN     mylistagg_type,\n     returnValue OUT    VARCHAR2,\n     flags       IN     NUMBER\n  ) RETURN NUMBER IS\n     l_sorted_strings string_tabtype;\n     l_return_value VARCHAR2(2000);\n  BEGIN\n    SELECT COLUMN_VALUE \n      BULK COLLECT INTO l_sorted_strings \n      FROM TABLE(strings) \n     ORDER BY COLUMN_VALUE;\n    FOR i IN 1 .. l_sorted_strings.COUNT\n    LOOP\n       IF i &gt; 1 THEN\n          l_return_value := l_return_value || ', ';\n       END IF;\n       l_return_value := l_return_value || l_sorted_strings(i);\n    END LOOP;\n    returnValue := l_return_value;\n    RETURN ODCIConst.Success;  \n  END ODCIAggregateTerminate;\n\n  MEMBER FUNCTION ODCIAggregateMerge(\n     SELF        IN OUT  mylistagg_type,\n     ctx2        IN      mylistagg_type\n  ) RETURN NUMBER IS\n  BEGIN\n    FOR i IN 1 .. ctx2.strings.COUNT \n    LOOP\n       SELF.strings.EXTEND;\n       SELF.strings(SELF.strings.COUNT) := ctx2.strings(i);\n    END LOOP;\n    RETURN ODCIConst.Success;\n  END ODCIAggregateMerge;\nEND;\n\/\n\nCREATE OR REPLACE FUNCTION mylistagg (\n   in_string IN VARCHAR2\n) RETURN VARCHAR2 PARALLEL_ENABLE AGGREGATE USING mylistagg_type;\n\/\n\nSELECT d.deptno, d.dname, mylistagg(e.ename) AS ename_list\n  FROM dept d\n  LEFT JOIN emp e ON e.deptno = d.deptno\n GROUP BY d.deptno, d.dname\n ORDER BY d.deptno, d.dname;\" style=\"color:#D4D4D4;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki dark-plus\" style=\"background-color: #1E1E1E\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #569CD6\">CREATE OR REPLACE<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">TYPE<\/span><span style=\"color: #D4D4D4\"> string_tabtype <\/span><span style=\"color: #569CD6\">AS<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">TABLE<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">OF<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">VARCHAR2<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #B5CEA8\">2000<\/span><span style=\"color: #D4D4D4\">);<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #569CD6\">CREATE OR REPLACE<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">TYPE<\/span><span style=\"color: #D4D4D4\"> mylistagg_type <\/span><span style=\"color: #569CD6\">AS<\/span><span style=\"color: #D4D4D4\"> OBJECT (<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">  strings string_tabtype,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">  STATIC FUNCTION ODCIAggregateInitialize(<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">     sctx        <\/span><span style=\"color: #569CD6\">IN<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">OUT<\/span><span style=\"color: #D4D4D4\"> mylistagg_type<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">  ) <\/span><span style=\"color: #C586C0\">RETURN<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">NUMBER<\/span><span style=\"color: #D4D4D4\">,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">  MEMBER FUNCTION ODCIAggregateIterate(<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">     SELF        <\/span><span style=\"color: #569CD6\">IN<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">OUT<\/span><span style=\"color: #D4D4D4\"> mylistagg_type,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">     value       <\/span><span style=\"color: #569CD6\">IN<\/span><span style=\"color: #D4D4D4\">     <\/span><span style=\"color: #569CD6\">VARCHAR2<\/span><span style=\"color: #D4D4D4\"> <\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">  ) <\/span><span style=\"color: #C586C0\">RETURN<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">NUMBER<\/span><span style=\"color: #D4D4D4\">,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">  MEMBER FUNCTION ODCIAggregateTerminate(<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">     SELF        <\/span><span style=\"color: #569CD6\">IN<\/span><span style=\"color: #D4D4D4\">     mylistagg_type,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">     returnValue <\/span><span style=\"color: #569CD6\">OUT<\/span><span style=\"color: #D4D4D4\">    <\/span><span style=\"color: #569CD6\">VARCHAR2<\/span><span style=\"color: #D4D4D4\">,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">     flags       <\/span><span style=\"color: #569CD6\">IN<\/span><span style=\"color: #D4D4D4\">     <\/span><span style=\"color: #569CD6\">NUMBER<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">  ) <\/span><span style=\"color: #C586C0\">RETURN<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">NUMBER<\/span><span style=\"color: #D4D4D4\">,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">  MEMBER FUNCTION ODCIAggregateMerge(<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">     SELF        <\/span><span style=\"color: #569CD6\">IN<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">OUT<\/span><span style=\"color: #D4D4D4\">  mylistagg_type,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">     ctx2        <\/span><span style=\"color: #569CD6\">IN<\/span><span style=\"color: #D4D4D4\">      mylistagg_type)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    <\/span><span style=\"color: #C586C0\">RETURN<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">NUMBER<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">);<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #569CD6\">CREATE OR REPLACE<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">TYPE<\/span><span style=\"color: #D4D4D4\"> BODY mylistagg_type <\/span><span style=\"color: #569CD6\">IS<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">  STATIC FUNCTION ODCIAggregateInitialize(<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">     sctx        <\/span><span style=\"color: #569CD6\">IN<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">OUT<\/span><span style=\"color: #D4D4D4\"> mylistagg_type<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">  ) <\/span><span style=\"color: #C586C0\">RETURN<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">NUMBER<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">IS<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">  <\/span><span style=\"color: #569CD6\">BEGIN<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    sctx := mylistagg_type(string_tabtype());<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    <\/span><span style=\"color: #C586C0\">RETURN<\/span><span style=\"color: #D4D4D4\"> ODCIConst.Success;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">  <\/span><span style=\"color: #569CD6\">END<\/span><span style=\"color: #D4D4D4\">;<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">  MEMBER FUNCTION ODCIAggregateIterate(<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">     SELF        <\/span><span style=\"color: #569CD6\">IN<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">OUT<\/span><span style=\"color: #D4D4D4\"> mylistagg_type,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">     value       <\/span><span style=\"color: #569CD6\">IN<\/span><span style=\"color: #D4D4D4\">     <\/span><span style=\"color: #569CD6\">VARCHAR2<\/span><span style=\"color: #D4D4D4\"> <\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">  ) <\/span><span style=\"color: #C586C0\">RETURN<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">NUMBER<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">IS<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">  <\/span><span style=\"color: #569CD6\">BEGIN<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    SELF.strings<\/span><span style=\"color: #DCDCAA\">.EXTEND<\/span><span style=\"color: #D4D4D4\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    SELF.strings(SELF.strings<\/span><span style=\"color: #DCDCAA\">.COUNT<\/span><span style=\"color: #D4D4D4\">) := VALUE;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    <\/span><span style=\"color: #C586C0\">RETURN<\/span><span style=\"color: #D4D4D4\"> ODCIConst.Success;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">  <\/span><span style=\"color: #569CD6\">END<\/span><span style=\"color: #D4D4D4\"> ODCIAggregateIterate;<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">  MEMBER FUNCTION ODCIAggregateTerminate(<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">     SELF        <\/span><span style=\"color: #569CD6\">IN<\/span><span style=\"color: #D4D4D4\">     mylistagg_type,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">     returnValue <\/span><span style=\"color: #569CD6\">OUT<\/span><span style=\"color: #D4D4D4\">    <\/span><span style=\"color: #569CD6\">VARCHAR2<\/span><span style=\"color: #D4D4D4\">,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">     flags       <\/span><span style=\"color: #569CD6\">IN<\/span><span style=\"color: #D4D4D4\">     <\/span><span style=\"color: #569CD6\">NUMBER<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">  ) <\/span><span style=\"color: #C586C0\">RETURN<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">NUMBER<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">IS<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">     <\/span><span style=\"color: #9CDCFE\">l_sorted_strings<\/span><span style=\"color: #D4D4D4\"> string_tabtype;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">     <\/span><span style=\"color: #9CDCFE\">l_return_value<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">VARCHAR2<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #B5CEA8\">2000<\/span><span style=\"color: #D4D4D4\">);<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">  <\/span><span style=\"color: #569CD6\">BEGIN<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    <\/span><span style=\"color: #569CD6\">SELECT<\/span><span style=\"color: #D4D4D4\"> COLUMN_VALUE <\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">      <\/span><span style=\"color: #DCDCAA\">BULK COLLECT<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">INTO<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">l_sorted_strings<\/span><span style=\"color: #D4D4D4\"> <\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">      <\/span><span style=\"color: #569CD6\">FROM<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">TABLE<\/span><span style=\"color: #D4D4D4\">(strings) <\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">     <\/span><span style=\"color: #569CD6\">ORDER BY<\/span><span style=\"color: #D4D4D4\"> COLUMN_VALUE;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    <\/span><span style=\"color: #C586C0\">FOR<\/span><span style=\"color: #D4D4D4\"> i <\/span><span style=\"color: #569CD6\">IN<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #B5CEA8\">1<\/span><span style=\"color: #D4D4D4\"> .. <\/span><span style=\"color: #9CDCFE\">l_sorted_strings<\/span><span style=\"color: #DCDCAA\">.COUNT<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    <\/span><span style=\"color: #C586C0\">LOOP<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">       <\/span><span style=\"color: #C586C0\">IF<\/span><span style=\"color: #D4D4D4\"> i &gt; <\/span><span style=\"color: #B5CEA8\">1<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">THEN<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">          <\/span><span style=\"color: #9CDCFE\">l_return_value<\/span><span style=\"color: #D4D4D4\"> := <\/span><span style=\"color: #9CDCFE\">l_return_value<\/span><span style=\"color: #D4D4D4\"> || <\/span><span style=\"color: #CE9178\">&#39;, &#39;<\/span><span style=\"color: #D4D4D4\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">       <\/span><span style=\"color: #C586C0\">END IF<\/span><span style=\"color: #D4D4D4\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">       <\/span><span style=\"color: #9CDCFE\">l_return_value<\/span><span style=\"color: #D4D4D4\"> := <\/span><span style=\"color: #9CDCFE\">l_return_value<\/span><span style=\"color: #D4D4D4\"> || <\/span><span style=\"color: #9CDCFE\">l_sorted_strings<\/span><span style=\"color: #D4D4D4\">(i);<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    <\/span><span style=\"color: #C586C0\">END LOOP<\/span><span style=\"color: #D4D4D4\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    returnValue := <\/span><span style=\"color: #9CDCFE\">l_return_value<\/span><span style=\"color: #D4D4D4\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    <\/span><span style=\"color: #C586C0\">RETURN<\/span><span style=\"color: #D4D4D4\"> ODCIConst.Success;  <\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">  <\/span><span style=\"color: #569CD6\">END<\/span><span style=\"color: #D4D4D4\"> ODCIAggregateTerminate;<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">  MEMBER FUNCTION ODCIAggregateMerge(<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">     SELF        <\/span><span style=\"color: #569CD6\">IN<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">OUT<\/span><span style=\"color: #D4D4D4\">  mylistagg_type,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">     ctx2        <\/span><span style=\"color: #569CD6\">IN<\/span><span style=\"color: #D4D4D4\">      mylistagg_type<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">  ) <\/span><span style=\"color: #C586C0\">RETURN<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">NUMBER<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">IS<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">  <\/span><span style=\"color: #569CD6\">BEGIN<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    <\/span><span style=\"color: #C586C0\">FOR<\/span><span style=\"color: #D4D4D4\"> i <\/span><span style=\"color: #569CD6\">IN<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #B5CEA8\">1<\/span><span style=\"color: #D4D4D4\"> .. ctx2.strings<\/span><span style=\"color: #DCDCAA\">.COUNT<\/span><span style=\"color: #D4D4D4\"> <\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    <\/span><span style=\"color: #C586C0\">LOOP<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">       SELF.strings<\/span><span style=\"color: #DCDCAA\">.EXTEND<\/span><span style=\"color: #D4D4D4\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">       SELF.strings(SELF.strings<\/span><span style=\"color: #DCDCAA\">.COUNT<\/span><span style=\"color: #D4D4D4\">) := ctx2.strings(i);<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    <\/span><span style=\"color: #C586C0\">END LOOP<\/span><span style=\"color: #D4D4D4\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    <\/span><span style=\"color: #C586C0\">RETURN<\/span><span style=\"color: #D4D4D4\"> ODCIConst.Success;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">  <\/span><span style=\"color: #569CD6\">END<\/span><span style=\"color: #D4D4D4\"> ODCIAggregateMerge;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #569CD6\">END<\/span><span style=\"color: #D4D4D4\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">\/<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #569CD6\">CREATE OR REPLACE<\/span><span style=\"color: #D4D4D4\"> FUNCTION mylistagg (<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">   in_string <\/span><span style=\"color: #569CD6\">IN<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">VARCHAR2<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">) <\/span><span style=\"color: #C586C0\">RETURN<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">VARCHAR2<\/span><span style=\"color: #D4D4D4\"> PARALLEL_ENABLE AGGREGATE <\/span><span style=\"color: #569CD6\">USING<\/span><span style=\"color: #D4D4D4\"> mylistagg_type;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">\/<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #569CD6\">SELECT<\/span><span style=\"color: #D4D4D4\"> d.deptno, d.dname, mylistagg(e.ename) <\/span><span style=\"color: #569CD6\">AS<\/span><span style=\"color: #D4D4D4\"> ename_list<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">  <\/span><span style=\"color: #569CD6\">FROM<\/span><span style=\"color: #D4D4D4\"> dept d<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">  <\/span><span style=\"color: #569CD6\">LEFT JOIN<\/span><span style=\"color: #D4D4D4\"> emp e <\/span><span style=\"color: #569CD6\">ON<\/span><span style=\"color: #D4D4D4\"> e.deptno = d.deptno<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">GROUP BY<\/span><span style=\"color: #D4D4D4\"> d.deptno, d.dname<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">ORDER BY<\/span><span style=\"color: #D4D4D4\"> d.deptno, d.dname;<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<h3 class=\"wp-block-heading\">Oracle9<em>i<\/em> Release 2<\/h3>\n\n\n\n<p>Version 9i Release 2 came with SQL\/XML support and the function XMLAGG which replaces SYS_XMLAGG and allows sorting the elements to be aggregated (see line 7).<br \/><a name=\"6\"><\/a><\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#2b2b2b;color:#c7c7c7\">6) Oracle9iR2 XMLAGG\/String Style<\/span><span role=\"button\" tabindex=\"0\" data-code=\"SELECT d.deptno,\n       d.dname,\n       RTRIM(\n          XMLAGG(\n             XMLELEMENT(\n                &quot;e&quot;, e.ename || ', '\n             ) ORDER BY e.ename\n          ).EXTRACT(\n             '\/e\/text()'\n          ).getStringVal(), ', '\n       ) AS ename_list\n  FROM dept d\n  LEFT JOIN emp e ON e.deptno = d.deptno\n GROUP BY d.deptno, d.dname\n ORDER BY d.deptno, d.dname;\" style=\"color:#D4D4D4;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki dark-plus\" style=\"background-color: #1E1E1E\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #569CD6\">SELECT<\/span><span style=\"color: #D4D4D4\"> d.deptno,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">       d.dname,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">       <\/span><span style=\"color: #DCDCAA\">RTRIM<\/span><span style=\"color: #D4D4D4\">(<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">          <\/span><span style=\"color: #DCDCAA\">XMLAGG<\/span><span style=\"color: #D4D4D4\">(<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">             <\/span><span style=\"color: #DCDCAA\">XMLELEMENT<\/span><span style=\"color: #D4D4D4\">(<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">                <\/span><span style=\"color: #CE9178\">&quot;e&quot;<\/span><span style=\"color: #D4D4D4\">, e.ename || <\/span><span style=\"color: #CE9178\">&#39;, &#39;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">             ) <\/span><span style=\"color: #569CD6\">ORDER BY<\/span><span style=\"color: #D4D4D4\"> e.ename<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">          ).<\/span><span style=\"color: #DCDCAA\">EXTRACT<\/span><span style=\"color: #D4D4D4\">(<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">             <\/span><span style=\"color: #CE9178\">&#39;\/e\/text()&#39;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">          ).getStringVal(), <\/span><span style=\"color: #CE9178\">&#39;, &#39;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">       ) <\/span><span style=\"color: #569CD6\">AS<\/span><span style=\"color: #D4D4D4\"> ename_list<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">  <\/span><span style=\"color: #569CD6\">FROM<\/span><span style=\"color: #D4D4D4\"> dept d<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">  <\/span><span style=\"color: #569CD6\">LEFT JOIN<\/span><span style=\"color: #D4D4D4\"> emp e <\/span><span style=\"color: #569CD6\">ON<\/span><span style=\"color: #D4D4D4\"> e.deptno = d.deptno<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">GROUP BY<\/span><span style=\"color: #D4D4D4\"> d.deptno, d.dname<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">ORDER BY<\/span><span style=\"color: #D4D4D4\"> d.deptno, d.dname;<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p>One may argue that using RTRIM to get rid of the last comma is not the way to interact with XML, especially since SQL\/XML supports XSLT. But probably no one can deny that writing the appropriate stylesheet is a bit more complex and time-consuming. Nonetheless, here&#8217;s an XSLT example:<br \/><a name=\"7\"><\/a><\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#2b2b2b;color:#c7c7c7\">7) Oracle9iR2 XMLAGG\/XSLT Style<\/span><span role=\"button\" tabindex=\"0\" data-code=\"SELECT d.deptno,\n       d.dname,\n       XMLTRANSFORM(\n          XMLAGG(\n             XMLELEMENT(\n                &quot;e&quot;, e.ename\n             ) ORDER BY e.ename\n          ), '<xsl:stylesheet version=&quot;1.0&quot; xmlns:xsl=&quot;http:\/\/www.w3.org\/1999\/XSL\/Transform&quot;&gt;\n                 <xsl:output method=&quot;text&quot;\/&gt;\n                 <xsl:template match=&quot;\/&quot;&gt;\n                    <xsl:for-each select=&quot;*&quot;&gt;\n                       <xsl:if test=&quot;position() != 1&quot;&gt;\n                          <xsl:value-of select=&quot;'', ''&quot;\/&gt;\n                       <\/xsl:if&gt;\n                       <xsl:value-of select=&quot;.&quot;\/&gt;\n                    <\/xsl:for-each&gt;\n                 <\/xsl:template&gt;\n              <\/xsl:stylesheet&gt;'\n       ).getStringVal() AS ename_list\n  FROM dept d\n  LEFT JOIN emp e ON e.deptno = d.deptno\n GROUP BY d.deptno, d.dname\n ORDER BY d.deptno, d.dname;\" style=\"color:#D4D4D4;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki dark-plus\" style=\"background-color: #1E1E1E\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #569CD6\">SELECT<\/span><span style=\"color: #D4D4D4\"> d.deptno,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">       d.dname,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">       XMLTRANSFORM(<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">          XMLAGG(<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">             XMLELEMENT(<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">                <\/span><span style=\"color: #CE9178\">&quot;e&quot;<\/span><span style=\"color: #D4D4D4\">, e.ename<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">             ) <\/span><span style=\"color: #569CD6\">ORDER BY<\/span><span style=\"color: #D4D4D4\"> e.ename<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">          ), <\/span><span style=\"color: #CE9178\">&#39;&lt;xsl:stylesheet version=&quot;1.0&quot; xmlns:xsl=&quot;http:\/\/www.w3.org\/1999\/XSL\/Transform&quot;&gt;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #CE9178\">                 &lt;xsl:output method=&quot;text&quot;\/&gt;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #CE9178\">                 &lt;xsl:template match=&quot;\/&quot;&gt;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #CE9178\">                    &lt;xsl:for-each select=&quot;*&quot;&gt;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #CE9178\">                       &lt;xsl:if test=&quot;position() != 1&quot;&gt;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #CE9178\">                          &lt;xsl:value-of select=&quot;&#39;&#39;, &#39;&#39;&quot;\/&gt;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #CE9178\">                       &lt;\/xsl:if&gt;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #CE9178\">                       &lt;xsl:value-of select=&quot;.&quot;\/&gt;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #CE9178\">                    &lt;\/xsl:for-each&gt;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #CE9178\">                 &lt;\/xsl:template&gt;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #CE9178\">              &lt;\/xsl:stylesheet&gt;&#39;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">       ).getStringVal() <\/span><span style=\"color: #569CD6\">AS<\/span><span style=\"color: #D4D4D4\"> ename_list<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">  <\/span><span style=\"color: #569CD6\">FROM<\/span><span style=\"color: #D4D4D4\"> dept d<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">  <\/span><span style=\"color: #569CD6\">LEFT JOIN<\/span><span style=\"color: #D4D4D4\"> emp e <\/span><span style=\"color: #569CD6\">ON<\/span><span style=\"color: #D4D4D4\"> e.deptno = d.deptno<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">GROUP BY<\/span><span style=\"color: #D4D4D4\"> d.deptno, d.dname<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">ORDER BY<\/span><span style=\"color: #D4D4D4\"> d.deptno, d.dname;<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<h3 class=\"wp-block-heading\">Oracle Database 11<em>g<\/em> Release 2<\/h3>\n\n\n\n<p>As mentioned at the beginning, in version 11g Release 2 Oracle finally introduced the aggregate function LISTAGG &nbsp;to conveniently aggregate strings.<br \/><a name=\"8\"><\/a><\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#2b2b2b;color:#c7c7c7\">8) Oracle 11gR2 ListAgg Style<\/span><span role=\"button\" tabindex=\"0\" data-code=\"SELECT d.deptno,\n       d.dname,\n       LISTAGG (\n          e.ename, ', '\n       ) WITHIN GROUP (\n          ORDER BY e.ename\n       ) AS ename_list\n  FROM dept d\n  LEFT JOIN emp e ON e.deptno = d.deptno\n GROUP BY d.deptno, d.dname\n ORDER BY d.deptno, d.dname;\" style=\"color:#D4D4D4;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki dark-plus\" style=\"background-color: #1E1E1E\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #569CD6\">SELECT<\/span><span style=\"color: #D4D4D4\"> d.deptno,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">       d.dname,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">       LISTAGG (<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">          e.ename, <\/span><span style=\"color: #CE9178\">&#39;, &#39;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">       ) <\/span><span style=\"color: #569CD6\">WITHIN<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #DCDCAA\">GROUP<\/span><span style=\"color: #D4D4D4\"> (<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">          <\/span><span style=\"color: #569CD6\">ORDER BY<\/span><span style=\"color: #D4D4D4\"> e.ename<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">       ) <\/span><span style=\"color: #569CD6\">AS<\/span><span style=\"color: #D4D4D4\"> ename_list<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">  <\/span><span style=\"color: #569CD6\">FROM<\/span><span style=\"color: #D4D4D4\"> dept d<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">  <\/span><span style=\"color: #569CD6\">LEFT JOIN<\/span><span style=\"color: #D4D4D4\"> emp e <\/span><span style=\"color: #569CD6\">ON<\/span><span style=\"color: #D4D4D4\"> e.deptno = d.deptno<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">GROUP BY<\/span><span style=\"color: #D4D4D4\"> d.deptno, d.dname<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #569CD6\">ORDER BY<\/span><span style=\"color: #D4D4D4\"> d.deptno, d.dname;<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<h3 class=\"wp-block-heading\">Performance Comparison<\/h3>\n\n\n\n<p>To compare the runtime performance of the different solution approaches I created 1 million rows in the dept table and 5 million rows in the emp table using <a href=\"\/\/www.salvis.com\/blog\/wp-content\/uploads\/2012\/12\/00_Setup_More_Data.sql_.txt\">this script<\/a>&nbsp;and measured the second serial execution of a CREATE TABLE AS SELECT&nbsp;statement for each of the 8 described approaches against my 11.2.0.3 instance. The following figure summarizes the results.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><a href=\"\/\/www.salvis.com\/blog\/wp-content\/uploads\/2012\/12\/RuntimeCSV1.png\"><img wpfc-lazyload-disable=\"true\" loading=\"lazy\" decoding=\"async\" width=\"768\" height=\"605\" src=\"\/\/www.salvis.com\/blog\/wp-content\/uploads\/2012\/12\/RuntimeCSV1.png\" alt=\"RuntimeCSV\" class=\"wp-image-768\" srcset=\"https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2012\/12\/RuntimeCSV1.png 768w, https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2012\/12\/RuntimeCSV1-300x236.png 300w\" sizes=\"auto, (max-width:767px) 480px, (max-width:768px) 100vw, 768px\" \/><\/a><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\">Conclusion<\/h3>\n\n\n\n<p>A lot of things have changed in the Oracle Database area since Oracle7, even in the niche of string aggregation. I recommend using LISTAGG (<a href=\"#8\">8<\/a>) whenever possible and avoid the use of SYS_XMLAGG (<a href=\"#4\">4<\/a>) or XSLT (<a href=\"#7\">7<\/a>) for string aggregation. The Collection Type (<a href=\"#2\">2<\/a>) approach is a good alternative if you do not mind creating helper objects otherwise use XMLAGG (<a href=\"#6\">6<\/a>).<\/p>\n","protected":false},"excerpt":{"rendered":"<p>From time to time I&#8217;m asked to aggregate strings from multiple records into a single column using SQL. Here&#8217;s an example, showing a comma-separated list of ordered employee names per department based on the famous EMP and DEPT tables. Oracle introduced the aggregate function LISTAGG for that purpose in 11.2. If you<span class=\"excerpt-hellip\"> [\u2026]<\/span><\/p>\n","protected":false},"author":1,"featured_media":768,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[5],"tags":[27,25,13,85],"class_list":["post-207","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oracle","tag-csv","tag-performance","tag-plsql","tag-sql"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.3 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Building Comma Separated Values with Oracle &amp; SQL - Philipp Salvisberg&#039;s Blog<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.salvis.com\/blog\/2012\/12\/23\/building-comma-separated-values-with-oracle-sql\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Building Comma Separated Values with Oracle &amp; SQL - Philipp Salvisberg&#039;s Blog\" \/>\n<meta property=\"og:description\" content=\"From time to time I&#8217;m asked to aggregate strings from multiple records into a single column using SQL. Here&#8217;s an example, showing a comma-separated list of ordered employee names per department based on the famous EMP and DEPT tables. Oracle introduced the aggregate function LISTAGG for that purpose in 11.2. If you [\u2026]\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.salvis.com\/blog\/2012\/12\/23\/building-comma-separated-values-with-oracle-sql\/\" \/>\n<meta property=\"og:site_name\" content=\"Philipp Salvisberg&#039;s Blog\" \/>\n<meta property=\"article:published_time\" content=\"2012-12-22T23:21:10+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2023-11-08T18:59:11+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2012\/12\/RuntimeCSV1.png\" \/>\n\t<meta property=\"og:image:width\" content=\"768\" \/>\n\t<meta property=\"og:image:height\" content=\"605\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"Philipp Salvisberg\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@phsalvisberg\" \/>\n<meta name=\"twitter:site\" content=\"@phsalvisberg\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Philipp Salvisberg\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"4 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/www.salvis.com\\\/blog\\\/2012\\\/12\\\/23\\\/building-comma-separated-values-with-oracle-sql\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.salvis.com\\\/blog\\\/2012\\\/12\\\/23\\\/building-comma-separated-values-with-oracle-sql\\\/\"},\"author\":{\"name\":\"Philipp Salvisberg\",\"@id\":\"https:\\\/\\\/www.salvis.com\\\/blog\\\/#\\\/schema\\\/person\\\/34352245c48678b1a5a05d4bc1339515\"},\"headline\":\"Building Comma Separated Values with Oracle &#038; SQL\",\"datePublished\":\"2012-12-22T23:21:10+00:00\",\"dateModified\":\"2023-11-08T18:59:11+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/www.salvis.com\\\/blog\\\/2012\\\/12\\\/23\\\/building-comma-separated-values-with-oracle-sql\\\/\"},\"wordCount\":545,\"commentCount\":2,\"publisher\":{\"@id\":\"https:\\\/\\\/www.salvis.com\\\/blog\\\/#\\\/schema\\\/person\\\/34352245c48678b1a5a05d4bc1339515\"},\"image\":{\"@id\":\"https:\\\/\\\/www.salvis.com\\\/blog\\\/2012\\\/12\\\/23\\\/building-comma-separated-values-with-oracle-sql\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/www.salvis.com\\\/blog\\\/wp-content\\\/uploads\\\/2012\\\/12\\\/RuntimeCSV1.png\",\"keywords\":[\"CSV\",\"Performance\",\"PL\\\/SQL\",\"SQL\"],\"articleSection\":[\"Oracle\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/www.salvis.com\\\/blog\\\/2012\\\/12\\\/23\\\/building-comma-separated-values-with-oracle-sql\\\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/www.salvis.com\\\/blog\\\/2012\\\/12\\\/23\\\/building-comma-separated-values-with-oracle-sql\\\/\",\"url\":\"https:\\\/\\\/www.salvis.com\\\/blog\\\/2012\\\/12\\\/23\\\/building-comma-separated-values-with-oracle-sql\\\/\",\"name\":\"Building Comma Separated Values with Oracle & SQL - Philipp Salvisberg&#039;s Blog\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.salvis.com\\\/blog\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/www.salvis.com\\\/blog\\\/2012\\\/12\\\/23\\\/building-comma-separated-values-with-oracle-sql\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/www.salvis.com\\\/blog\\\/2012\\\/12\\\/23\\\/building-comma-separated-values-with-oracle-sql\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/www.salvis.com\\\/blog\\\/wp-content\\\/uploads\\\/2012\\\/12\\\/RuntimeCSV1.png\",\"datePublished\":\"2012-12-22T23:21:10+00:00\",\"dateModified\":\"2023-11-08T18:59:11+00:00\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/www.salvis.com\\\/blog\\\/2012\\\/12\\\/23\\\/building-comma-separated-values-with-oracle-sql\\\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/www.salvis.com\\\/blog\\\/2012\\\/12\\\/23\\\/building-comma-separated-values-with-oracle-sql\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/www.salvis.com\\\/blog\\\/2012\\\/12\\\/23\\\/building-comma-separated-values-with-oracle-sql\\\/#primaryimage\",\"url\":\"https:\\\/\\\/www.salvis.com\\\/blog\\\/wp-content\\\/uploads\\\/2012\\\/12\\\/RuntimeCSV1.png\",\"contentUrl\":\"https:\\\/\\\/www.salvis.com\\\/blog\\\/wp-content\\\/uploads\\\/2012\\\/12\\\/RuntimeCSV1.png\",\"width\":768,\"height\":605},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/www.salvis.com\\\/blog\\\/2012\\\/12\\\/23\\\/building-comma-separated-values-with-oracle-sql\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/www.salvis.com\\\/blog\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Building Comma Separated Values with Oracle &#038; SQL\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/www.salvis.com\\\/blog\\\/#website\",\"url\":\"https:\\\/\\\/www.salvis.com\\\/blog\\\/\",\"name\":\"Philipp Salvisberg&#039;s Blog\",\"description\":\"Database-centric development\",\"publisher\":{\"@id\":\"https:\\\/\\\/www.salvis.com\\\/blog\\\/#\\\/schema\\\/person\\\/34352245c48678b1a5a05d4bc1339515\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/www.salvis.com\\\/blog\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":[\"Person\",\"Organization\"],\"@id\":\"https:\\\/\\\/www.salvis.com\\\/blog\\\/#\\\/schema\\\/person\\\/34352245c48678b1a5a05d4bc1339515\",\"name\":\"Philipp Salvisberg\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/www.salvis.com\\\/blog\\\/wp-content\\\/uploads\\\/2010\\\/11\\\/phs_trivadis4.jpg\",\"url\":\"https:\\\/\\\/www.salvis.com\\\/blog\\\/wp-content\\\/uploads\\\/2010\\\/11\\\/phs_trivadis4.jpg\",\"contentUrl\":\"https:\\\/\\\/www.salvis.com\\\/blog\\\/wp-content\\\/uploads\\\/2010\\\/11\\\/phs_trivadis4.jpg\",\"width\":400,\"height\":400,\"caption\":\"Philipp Salvisberg\"},\"logo\":{\"@id\":\"https:\\\/\\\/www.salvis.com\\\/blog\\\/wp-content\\\/uploads\\\/2010\\\/11\\\/phs_trivadis4.jpg\"},\"sameAs\":[\"http:\\\/\\\/www.salvis.com\\\/\"]}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Building Comma Separated Values with Oracle & SQL - Philipp Salvisberg&#039;s Blog","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.salvis.com\/blog\/2012\/12\/23\/building-comma-separated-values-with-oracle-sql\/","og_locale":"en_US","og_type":"article","og_title":"Building Comma Separated Values with Oracle & SQL - Philipp Salvisberg&#039;s Blog","og_description":"From time to time I&#8217;m asked to aggregate strings from multiple records into a single column using SQL. Here&#8217;s an example, showing a comma-separated list of ordered employee names per department based on the famous EMP and DEPT tables. Oracle introduced the aggregate function LISTAGG for that purpose in 11.2. If you [\u2026]","og_url":"https:\/\/www.salvis.com\/blog\/2012\/12\/23\/building-comma-separated-values-with-oracle-sql\/","og_site_name":"Philipp Salvisberg&#039;s Blog","article_published_time":"2012-12-22T23:21:10+00:00","article_modified_time":"2023-11-08T18:59:11+00:00","og_image":[{"width":768,"height":605,"url":"https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2012\/12\/RuntimeCSV1.png","type":"image\/png"}],"author":"Philipp Salvisberg","twitter_card":"summary_large_image","twitter_creator":"@phsalvisberg","twitter_site":"@phsalvisberg","twitter_misc":{"Written by":"Philipp Salvisberg","Est. reading time":"4 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.salvis.com\/blog\/2012\/12\/23\/building-comma-separated-values-with-oracle-sql\/#article","isPartOf":{"@id":"https:\/\/www.salvis.com\/blog\/2012\/12\/23\/building-comma-separated-values-with-oracle-sql\/"},"author":{"name":"Philipp Salvisberg","@id":"https:\/\/www.salvis.com\/blog\/#\/schema\/person\/34352245c48678b1a5a05d4bc1339515"},"headline":"Building Comma Separated Values with Oracle &#038; SQL","datePublished":"2012-12-22T23:21:10+00:00","dateModified":"2023-11-08T18:59:11+00:00","mainEntityOfPage":{"@id":"https:\/\/www.salvis.com\/blog\/2012\/12\/23\/building-comma-separated-values-with-oracle-sql\/"},"wordCount":545,"commentCount":2,"publisher":{"@id":"https:\/\/www.salvis.com\/blog\/#\/schema\/person\/34352245c48678b1a5a05d4bc1339515"},"image":{"@id":"https:\/\/www.salvis.com\/blog\/2012\/12\/23\/building-comma-separated-values-with-oracle-sql\/#primaryimage"},"thumbnailUrl":"https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2012\/12\/RuntimeCSV1.png","keywords":["CSV","Performance","PL\/SQL","SQL"],"articleSection":["Oracle"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/www.salvis.com\/blog\/2012\/12\/23\/building-comma-separated-values-with-oracle-sql\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/www.salvis.com\/blog\/2012\/12\/23\/building-comma-separated-values-with-oracle-sql\/","url":"https:\/\/www.salvis.com\/blog\/2012\/12\/23\/building-comma-separated-values-with-oracle-sql\/","name":"Building Comma Separated Values with Oracle & SQL - Philipp Salvisberg&#039;s Blog","isPartOf":{"@id":"https:\/\/www.salvis.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.salvis.com\/blog\/2012\/12\/23\/building-comma-separated-values-with-oracle-sql\/#primaryimage"},"image":{"@id":"https:\/\/www.salvis.com\/blog\/2012\/12\/23\/building-comma-separated-values-with-oracle-sql\/#primaryimage"},"thumbnailUrl":"https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2012\/12\/RuntimeCSV1.png","datePublished":"2012-12-22T23:21:10+00:00","dateModified":"2023-11-08T18:59:11+00:00","breadcrumb":{"@id":"https:\/\/www.salvis.com\/blog\/2012\/12\/23\/building-comma-separated-values-with-oracle-sql\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.salvis.com\/blog\/2012\/12\/23\/building-comma-separated-values-with-oracle-sql\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.salvis.com\/blog\/2012\/12\/23\/building-comma-separated-values-with-oracle-sql\/#primaryimage","url":"https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2012\/12\/RuntimeCSV1.png","contentUrl":"https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2012\/12\/RuntimeCSV1.png","width":768,"height":605},{"@type":"BreadcrumbList","@id":"https:\/\/www.salvis.com\/blog\/2012\/12\/23\/building-comma-separated-values-with-oracle-sql\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.salvis.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Building Comma Separated Values with Oracle &#038; SQL"}]},{"@type":"WebSite","@id":"https:\/\/www.salvis.com\/blog\/#website","url":"https:\/\/www.salvis.com\/blog\/","name":"Philipp Salvisberg&#039;s Blog","description":"Database-centric development","publisher":{"@id":"https:\/\/www.salvis.com\/blog\/#\/schema\/person\/34352245c48678b1a5a05d4bc1339515"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.salvis.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":["Person","Organization"],"@id":"https:\/\/www.salvis.com\/blog\/#\/schema\/person\/34352245c48678b1a5a05d4bc1339515","name":"Philipp Salvisberg","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2010\/11\/phs_trivadis4.jpg","url":"https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2010\/11\/phs_trivadis4.jpg","contentUrl":"https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2010\/11\/phs_trivadis4.jpg","width":400,"height":400,"caption":"Philipp Salvisberg"},"logo":{"@id":"https:\/\/www.salvis.com\/blog\/wp-content\/uploads\/2010\/11\/phs_trivadis4.jpg"},"sameAs":["http:\/\/www.salvis.com\/"]}]}},"_links":{"self":[{"href":"https:\/\/www.salvis.com\/blog\/wp-json\/wp\/v2\/posts\/207","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.salvis.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.salvis.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.salvis.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.salvis.com\/blog\/wp-json\/wp\/v2\/comments?post=207"}],"version-history":[{"count":9,"href":"https:\/\/www.salvis.com\/blog\/wp-json\/wp\/v2\/posts\/207\/revisions"}],"predecessor-version":[{"id":12747,"href":"https:\/\/www.salvis.com\/blog\/wp-json\/wp\/v2\/posts\/207\/revisions\/12747"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.salvis.com\/blog\/wp-json\/wp\/v2\/media\/768"}],"wp:attachment":[{"href":"https:\/\/www.salvis.com\/blog\/wp-json\/wp\/v2\/media?parent=207"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.salvis.com\/blog\/wp-json\/wp\/v2\/categories?post=207"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.salvis.com\/blog\/wp-json\/wp\/v2\/tags?post=207"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}