【问题标题】:Duplicate values when splitting a string拆分字符串时出现重复值
【发布时间】:2023-01-12 00:19:39
【问题描述】:

我正在尝试为每个人创建一行,str 但我得到了额外的输出。

有人可以解释我做错了什么并告诉我如何解决它。

下面是我的测试案例和预期结果。感谢所有回答的人和您的专业知识。


with rws as (
  select 'Bob' person, 'AB,CR,DE' str from dual UNION ALL 
  select 'Jane' person, 'AB' str from dual 
)
  select person,
       regexp_substr (
           str,
           '[^,]+',
           1,
           level
         ) value
  from   rws
  connect by level <= 
    length ( str ) - length ( replace ( str, ',' ) ) + 1
ORDER BY person, str;

PERSON  VALUE
Bob AB
Bob CR
Bob DE
Bob DE
Bob CR
Jane AB

Expected results 
PERSON  VALUE
Bob AB
Bob CR
Bob DE
Jane AB

【问题讨论】:

    标签: sql string oracle


    【解决方案1】:

    这是一个选项:

    SQL> WITH
      2     rws
      3     AS
      4        (SELECT 'Bob' person, 'AB,CR,DE' str FROM DUAL
      5         UNION ALL
      6         SELECT 'Jane' person, 'AB' str FROM DUAL)
      7    SELECT person,
      8           REGEXP_SUBSTR (str,
      9                          '[^,]+',
     10                          1,
     11                          COLUMN_VALUE) VALUE
     12      FROM rws
     13           CROSS JOIN
     14           TABLE (
     15              CAST (
     16                 MULTISET (    SELECT LEVEL
     17                                 FROM DUAL
     18                           CONNECT BY LEVEL <= REGEXP_COUNT (str, ',') + 1)
     19                    AS SYS.odcinumberlist))
     20  ORDER BY person, str;
    
    PERS VALUE
    ---- --------
    Bob  AB
    Bob  CR
    Bob  DE
    Jane AB
    
    SQL>
    

    您的解决方案如果您应用SELECT DISTINCT(并修复了order by子句,但这是无关紧要的),则返回所需的结果,但这也会表现严重随着您正在处理的行数的增加。

    SQL> with rws as (
      2    select 'Bob' person, 'AB,CR,DE' str from dual UNION ALL
      3    select 'Jane' person, 'AB' str from dual
      4  )
      5    select distinct person,
      6         regexp_substr (
      7             str,
      8             '[^,]+',
      9             1,
     10             level
     11           ) value
     12    from   rws
     13    connect by level <=
     14      length ( str ) - length ( replace ( str, ',' ) ) + 1;
    
    PERS VALUE
    ---- --------
    Jane AB
    Bob  CR
    Bob  AB
    Bob  DE
    
    SQL>
    

    【讨论】:

      【解决方案2】:

      如果数据中没有引号,对于 12c+,您可以使用 JSON_TABLE 和横向连接而不是递归。

      with rws as (
        select 'Bob' person, 'AB,CR,DE' str from dual UNION ALL 
        select 'Jane' person, 'AB' str from dual union all
        select 'Mark', null from dual
      )
      select
        rws.person,
        l.val_splitted,
        l.rn
      from rws
        left join lateral (
          select *
          from json_table(
            '["' || replace(rws.str, ',', '","') || '"]',
            '$[*]'
            columns (
              val_splitted varchar2(10) path '$',
              rn for ordinality
            )
          )
        ) l
        on 1 = 1
      order by 1
      
      PERSON VAL_SPLITTED RN
      Bob AB 1
      Bob CR 2
      Bob DE 3
      Jane AB 1
      Mark 1

      【讨论】:

        【解决方案3】:

        原始查询的问题在于 connect-by 不止一次查看前几行 - 本质上,Bob 的第二级行也在为 Jane 选择第一行。这是一个相当众所周知的问题。你可以通过包含一个唯一的 ID 来避免这种情况(在这个例子中你必须依赖名称,并希望它是唯一的);但是那会循环,你可以通过添加一个非确定性函数调用来避免循环:

        ...
          connect by level <= 
            length ( str ) - length ( replace ( str, ',' ) ) + 1
          and prior person = person
          and prior dbms_random.value is not null
        ORDER BY person, str;
        

        您还可以使用递归子查询分解而不是分层查询:

        with rws as (
          select 'Bob' person, 'AB,CR,DE' str from dual UNION ALL 
          select 'Jane' person, 'AB' str from dual 
        ),
        rcte (person, str, cnt, lvl, value) as (
          select person, str, length ( str ) - length ( replace ( str, ',' ) ), 1,
               regexp_substr (
                   str,
                   '[^,]+',
                   1,
                   1
                 )
          from   rws
          union all
          select person, str, cnt, lvl + 1,
               regexp_substr (
                   str,
                   '[^,]+',
                   1,
                   lvl + 1
                 )
          from   rcte
          where  lvl <= cnt
        )
        select person, value
        from rcte
        order by person, value;
        

        fiddle

        但您可能会发现其他答案之一表现更好,或者至少易于理解和维护。

        顺便说一句,如果您有空元素(即两个相邻的逗号),您的正则表达式模式可能会导致问题; this this answer for an explanation

        【讨论】:

          猜你喜欢
          • 2018-02-09
          • 1970-01-01
          • 1970-01-01
          • 2017-07-22
          • 1970-01-01
          • 2017-04-09
          • 2014-10-14
          • 2010-11-03
          • 1970-01-01
          相关资源
          最近更新 更多