Отличие значений CSV с использованием REGEXP_REPLACE в оракуле

У меня есть таблица с данными, как показано ниже

Column A      Column B
-------------------------
1             POW
2             POW
1             POWPRO
1             PRO
2             PRO
1             PROUTL
1             TNEUTL
1             UTL
1             UTLTNE

И мне нужен вывод, как показано ниже

Выход

Column A      Column B

1,2           POW,POWPRO,PRO,PROUTL,TNEUTL,UTL,UTLTNE  

Я попробовал запрос ниже. Но выход разный.

select dbms_lob.substr( ltrim(REGEXP_REPLACE(REPLACE(
     REPLACE(
       XMLAGG(
         XMLELEMENT("A",COLUMN_A )
           ORDER BY COLUMN_A).getClobVal(),
         '<A>',','),
         '</A>',' '),'([^,]+)(,\1)+', '\1'),
dbms_lob.substr( ltrim(REGEXP_REPLACE(REPLACE(
     REPLACE(
       XMLAGG(
         XMLELEMENT("A",COLUMN_B )
           ORDER BY COLUMN_B).getClobVal(),
         '<A>',','),
         '</A>',' '),'([^,]+)(,\1)+', '\1') from table_name

Но выход

Column A     Column B
-------------------------------------------------
1,2          POW ,POWPRO ,PROUTL ,TNEUTL ,UTLTNE 

Я хочу использовать только regexp_replace для поиска шаблона. Пожалуйста, помогите мне.


person kernel_poi    schedule 22.06.2016    source источник
comment
Какая версия Оракла?   -  person jpmc26    schedule 23.06.2016
comment
Оракл версии 12 С   -  person kernel_poi    schedule 23.06.2016
comment
Тогда мой ответ должен сработать для вас.   -  person jpmc26    schedule 23.06.2016
comment
Почему dbms_lob и getClobVal()? Каков тип данных ваших столбцов?   -  person mathguy    schedule 23.06.2016


Ответы (2)


Вы можете использовать коллекции Oracle. CAST() в паре с COLLECT() может агрегировать значения в определяемую пользователем коллекцию, а затем SET() избавится от дубликатов. Затем вы можете использовать LISTAGG() для преобразования коллекции в строку.

Настройка Oracle:

CREATE TYPE intlist IS TABLE OF INT;
/

CREATE TYPE stringlist IS TABLE OF VARCHAR2(4000);
/

CREATE TABLE table_name ( ColA NUMBER(5,0), ColB VARCHAR2(20) );
INSERT INTO table_name
  SELECT 1, 'POW' FROM DUAL UNION ALL
  SELECT 2, 'POW' FROM DUAL UNION ALL
  SELECT 1, 'POWPRO' FROM DUAL UNION ALL
  SELECT 1, 'PRO' FROM DUAL UNION ALL
  SELECT 2, 'PRO' FROM DUAL UNION ALL
  SELECT 1, 'PROUTL' FROM DUAL UNION ALL
  SELECT 1, 'TNEUTL' FROM DUAL UNION ALL
  SELECT 1, 'UTL' FROM DUAL UNION ALL
  SELECT 1, 'UTLTNE' FROM DUAL;

Запрос:

SELECT ( SELECT LISTAGG( COLUMN_VALUE, ',' )
                  WITHIN GROUP ( ORDER BY COLUMN_VALUE )
         FROM   TABLE( ColA ) ) AS ColA,
       ( SELECT LISTAGG( COLUMN_VALUE, ',' )
                  WITHIN GROUP ( ORDER BY COLUMN_VALUE )
         FROM   TABLE( ColB ) ) AS ColB  
FROM   (
  SELECT SET( CAST( COLLECT( ColA ORDER BY ColA ) AS INTLIST ) ) ColA,
         SET( CAST( COLLECT( ColB ORDER BY ColB ) AS STRINGLIST ) ) ColB
  FROM   table_name
);

Вывод:

ColA ColB
---- ---------------------------------------
1,2  POW,POWPRO,PRO,PROUTL,TNEUTL,UTL,UTLTNE
person MT0    schedule 23.06.2016

Я собираюсь начать с предположения, что ваши фактические имена столбцов — A и B, а не "Column A" и "Column B". Если это предположение неверно, все, что вам нужно сделать, это изменить имена ниже.

Вы хотите LIST_AGG (доступно для Oracle 11g и выше):

SELECT
    LISTAGG(A, ',') WITHIN GROUP (ORDER BY A) AS A_VALUES
FROM TABLE_NAME;

Не обращайте внимания на бит WITHIN GROUP. Это «аналитическая функция», которая является просто именем Oracle для того, что другие БД вызывают оконные функции. Основная идея аналитической/оконной функции заключается в том, что она позволяет получить доступ к данным в соседней строке результатов, чтобы определить значение для текущей строки. Простым примером того, для чего они хороши, является накопительная сумма.

В данном случае то, что он аналитический/оконный, лишнее. Мы просто используем его для агрегирования. Однако мы должны предоставить часть WITHIN GROUP, чтобы предотвратить синтаксическую ошибку. Мы говорим ему, по какому порядку упорядочивать список, который в данном случае является просто столбцом, который мы агрегируем. Вы можете сделать намного больше с помощью аналитических/оконных функций, но здесь не место останавливаться на этих возможностях.

Все становится немного сложнее, так как вам нужны значения DISTINCT:

SELECT
    LISTAGG(A, ',') WITHIN GROUP (ORDER BY A) AS A_VALUES
FROM (
    SELECT DISTINCT A
    FROM TABLE_NAME
);

И еще сложнее, так как вам нужны оба столбца:

SELECT *
FROM (
    SELECT
        LISTAGG(A, ',') WITHIN GROUP (ORDER BY A) AS A_VALUES
    FROM (
        SELECT DISTINCT A
        FROM TABLE_NAME
    )
) A_VALS,
(
    SELECT
        LISTAGG(B, ',') WITHIN GROUP (ORDER BY B) AS B_VALUES
    FROM (
        SELECT DISTINCT B
        FROM TABLE_NAME
    )
) B_VALS

Этот последний возвращает вам то, что вы хотели. Он просто создает строку из различных значений для каждого столбца в подзапросе, а затем выполняет полное соединение (запятая, поскольку нет фильтров), чтобы объединить два столбца. Каждый подзапрос возвращает одну строку, поэтому в конечном результате вы получите только одну строку.

person jpmc26    schedule 22.06.2016