我们可以一起使用connect by子句和listagg函数吗

时间:2019-06-10 04:10:39

标签: sql oracle split

我有一列带有用##和组名分隔的城市值。我想按字母顺序对文本列进行排序

问题:

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 :(得分:0)

也许以下代码对您有用。

-- 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

希望这就是您想要的。

这里是Demo

答案 1 :(得分:0)

您遗漏了一点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>

答案 2 :(得分:0)

在SQL中,有多种将字符串拆分为行的方法。我的首选方法是使用递归子因子查询(非Oracle人群使用通用表函数或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个参数指示第一个捕获组包含应返回的值,而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;