Можем ли мы использовать функцию connect by clause и listagg вместе - PullRequest
0 голосов
/ 10 июня 2019

У меня есть столбец со значениями города, разделенными ## и именем группы. Я хочу отсортировать текстовый столбец в алфавитном порядке

Проблема:

group   | values
group1  | jammu##bhopal##chandigardh
group2  | Mumbai##kolkatta
group3  | bangalore

Ожидаемый результат

group   | values
group1  | bhopal##chandigardh##jammu
group2  | kolkatta##Mumbai
group3  | bangalore

Я пробовал ниже код

select group,listagg(city,'#') within group (order by city asc) as city
from (
      select group, regexp_substr(city,'[^##+',1, LEVEL) as city
     from (
           select group,city from city_group
          )
     connect by regexp_substr(city,'[^##+',1, LEVEL) us not null)
group by group

Код работает вечно и не дает результата.

Ответы [ 3 ]

0 голосов
/ 10 июня 2019

Вам не хватает немного JOIN, поэтому ваш код действительно работает вечно. Обратите внимание на строки № 10 - 12; это важно в вашем случае.

SQL> with test (c_group, c_values) as
  2    (select 'group1', 'jammu##bhopal##chandigardh' from dual union all
  3     select 'group2', 'mumbai##kolkatta'           from dual union all
  4     select 'group3', 'bangalore'                  from dual
  5    ),
  6  temp as
  7    (select c_group,
  8            regexp_substr(c_values, '[^##]+', 1, column_value) col
  9     from test join
 10          table(cast(multiset(select level from dual
 11                              connect by regexp_substr(c_values, '[^##]+', 1, level) is not null
 12                             ) as sys.odcinumberlist)) on 1 = 1
 13    )
 14  select c_group,
 15         listagg(col, '##') within group (order by col) result
 16  from temp
 17  group by c_group
 18  order by c_group;

C_GROU RESULT
------ ----------------------------------------
group1 bhopal##chandigardh##jammu
group2 kolkatta##mumbai
group3 bangalore

SQL>
0 голосов
/ 11 июня 2019

В SQL есть несколько способов разбить строки на строки. Мой предпочтительный метод - использовать рекурсивный подфакторный запрос (обычная табличная функция или CTE для толпы не оракула).

with city_group(grp, cities) as (
  select 'group1', 'jammu##bhopal##chandigardh' from dual union all
  select 'group2', 'Mumbai##kolkatta' from dual union all
  select 'group3', 'bangalore' from dual
), Recur (grp, cities, city, nxt, lst) as (
  -- Anchor Query
  select grp, cities
       , REGEXP_SUBSTR(cities,'(.+?)(##|$)',1,1,'',1)
       , REGEXP_INSTR(cities,'(.+?)(##|$)',1,1,1)
       , length(cities)
    from city_group

  -- Recursive part
  union all
  select grp, cities
       , REGEXP_SUBSTR(cities,'(.+?)(##|$)',nxt,1,'',1)
       , REGEXP_INSTR(cities,'(.+?)(##|$)',nxt,1,1)
       , lst
    from Recur
   where nxt <= lst
)
select grp
     , listagg(city,'##') within group (order by city)
  from Recur
  group by grp;

В приведенном выше коде якорный запрос возвращает первый элемент списка и устанавливает дополнительные столбцы для перебора списка значений. В частности, он возвращает следующие начальные позиции поиска (nxt) и позицию последнего (lst) символа в строке, используемой в качестве условия остановки для рекурсивной части.

Далее в рекурсивной части запроса последующие элементы списка возвращаются вместе с новой nxt начальной позицией.

Использование движущейся стартовой позиции уменьшает объем работы, необходимой для поиска строки, когда вы продвигаетесь дальше в список значений.

Теперь слово об используемом регулярном выражении. Вместо ошибочной поисковой строки [^##]+, которая функционально эквивалентна [^#]+ и которая может ошибочно идентифицировать одиночный # в качестве разделителей строк, я использовал не жадную группу захвата (.+?), за которой следует группа захвата, которая явно соответствует маркер разделителя строк или символ конца строки (##|$). Затем функция REGEXP_SUBSTR использует 6-й параметр, который указывает, что 1-я группа захвата содержит значение, которое должно быть возвращено, в то время как последний параметр функции REGEXP_INSTR указывает, что позиция символа, следующая за соответствующей подстрокой, должна быть возвращена.

В качестве окончательного бонусного решения, если в вашей БД установлена ​​последняя версия APEX, вы можете просто использовать табличную функцию APEX_STRING.split следующим образом:

with city_group(grp, cities) as (
  select 'group1', 'jammu##bhopal##chandigardh' from dual union all
  select 'group2', 'Mumbai##kolkatta' from dual union all
  select 'group3', 'bangalore' from dual
)
select grp
     , listagg(column_value,'##') within group (order by column_value)
  from city_group cg
  cross apply apex_string.split(cities,'##')
  group by grp;
0 голосов
/ 10 июня 2019

Возможно, следующий код будет вам полезен.

-- Data preparation

CREATE TABLE CITY_GROUP (GROUPS VARCHAR2(100), VALUE VARCHAR2(4000));

INSERT INTO CITY_GROUP VALUES('group1','jammu##bhopal##chandigardh');

INSERT INTO CITY_GROUP VALUES('group2','mumbai##kolkatta');

INSERT INTO CITY_GROUP VALUES('group3','bangalore');

-- Your query

SELECT
    GROUPS,
    LISTAGG(CITY, '##') WITHIN GROUP(
        ORDER BY
            CITY
    ) AS CITY
FROM
    (
        SELECT DISTINCT
            GROUPS   AS GROUPS,
            REGEXP_SUBSTR(VALUE, '[^##]+', 1, LEVEL) AS CITY
        FROM
            CITY_GROUP
        CONNECT BY
            REGEXP_SUBSTR(VALUE, '[^##]+', 1, LEVEL) IS NOT NULL
    )
GROUP BY
    GROUPS;

Выход

Output

Надеюсь, это то, что вы ищете.

Вот это Демо

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...