【问题标题】:oracle -- Split multiple comma separated values in oracle table to multiple rowsoracle -- 将 oracle 表中的多个逗号分隔值拆分为多行
【发布时间】:2013-09-17 05:06:11
【问题描述】:

我在使用 oracle 拆分查询时遇到问题。

在 oracle 查询中使用 connect by 和正则表达式将逗号分隔的数据拆分为多行时,我得到了更多的重复行。例如,实际上我的表有 150 行,其中两行有逗号分隔的字符串,所以总的来说我只需要 155 行,但我得到 2000 行。如果我使用 distinct 它工作正常,但我不希望查询结果中有重复的行。

我尝试了以下查询,但是它在查询结果中生成了重复的行:

WITH CTE AS (SELECT 'a,b,c,d,e' temp,1 slno  FROM DUAL
              UNION 
              SELECT 'f,g',2 from dual
              UNION 
               SELECT 'h',3 FROM DUAL)

SELECT TRIM(REGEXP_SUBSTR( TEMP, '[^,]+', 1, LEVEL)) ,SLNO FROM CTE 
CONNECT BY LEVEL <= LENGTH(REGEXP_REPLACE(temp, '[^,]+')) + 1

编辑

上述选择查询只能拆分单个逗号分隔的字符串,但是,在具有多行的表上执行时会产生重复行。如何限制重复行?

【问题讨论】:

  • 此问题与另一个标记为重复的问题不重复。另一个问题是拆分单行,这个问题是关于多行的。
  • 接受的答案使用了不合适的条件DBMS_RANDOM.VALUE IS NOT NULL。它只是防止循环循环,但是会出现一个直截了当的问题:dbms_random.VALUE 如何以及何时可以为空? 从逻辑上讲,它永远不会为空。更合适的解决方案是使用sys.odciNumberList 并防止循环循环。请在下面查看我的答案。

标签: sql regex oracle split


【解决方案1】:

添加一个独特的子句就可以了:

    WITH cte AS (
        SELECT 'a,b,c,d,e' temp, 1 slno FROM DUAL UNION 
        SELECT 'f,g',2 FROM DUAL UNION SELECT 'h',3 FROM DUAL
    ) SELECT UNIQUE(slno),REGEXP_SUBSTR(temp,'[^,]+', 1, LEVEL)temp FROM cte
    CONNECT BY LEVEL<=REGEXP_COUNT(temp, '[^,]+') ORDER BY slno;

【讨论】:

    【解决方案2】:

    我终于想出了这个答案

    WITH CTE AS (SELECT 'a,b,c,d,e' temp, 1 slno FROM DUAL
                  UNION
                  SELECT 'f,g' temp, 2 slno FROM DUAL
                  UNION
                  SELECT 'h' temp, 3 slno FROM DUAL)
    SELECT TRIM(REGEXP_SUBSTR(temp, '[^,]+', 1, level)), slno
    FROM CTE
    CONNECT BY level <= REGEXP_COUNT(temp, '[^,]+')
        AND PRIOR slno = slno
        AND PRIOR DBMS_RANDOM.VALUE IS NOT NULL
    

    【讨论】:

    • 关于您的评论here,为什么这会起作用并且不会产生重复的行?随机似乎可以防止循环循环。
    • 从逻辑上讲,PRIOR DBMS_RANDOM.VALUE IS NOT NULL 没有任何意义。 dbms_random.VALUE 如何以及何时可以为空?更好的方法是使用sys.odciNumberList。在此处查看一些演示 lalitkumarb.wordpress.com/2015/03/04/…
    【解决方案3】:

    接受的答案使用了不合适的条件DBMS_RANDOM.VALUE IS NOT NULL。它只是防止循环循环,但是会出现一个直截了当的问题:dbms_random.VALUE 如何以及何时可以为 null? 从逻辑上讲,它永远不会是 NULL。 p>

    更合适的解决方案是使用sys.odciNumberList并防止循环循环。

    例如,

    设置

    SQL> CREATE TABLE t (
      2    ID          NUMBER GENERATED ALWAYS AS IDENTITY,
      3    text        VARCHAR2(100)
      4  );
    
    Table created.
    
    SQL>
    SQL> INSERT INTO t (text) VALUES ('word1, word2, word3');
    
    1 row created.
    
    SQL> INSERT INTO t (text) VALUES ('word4, word5, word6');
    
    1 row created.
    
    SQL> INSERT INTO t (text) VALUES ('word7, word8, word9');
    
    1 row created.
    
    SQL> COMMIT;
    
    Commit complete.
    
    SQL>
    SQL> SELECT * FROM t;
    
            ID TEXT
    ---------- ----------------------------------------------
             1 word1, word2, word3
             2 word4, word5, word6
             3 word7, word8, word9
    
    SQL>
    

    必填查询:

    SQL> SELECT t.id,
      2         trim(regexp_substr(t.text, '[^,]+', 1, lines.column_value)) text
      3  FROM t,
      4    TABLE (CAST (MULTISET
      5    (SELECT LEVEL FROM dual CONNECT BY LEVEL <= regexp_count(t.text, ',')+1)
      6                 AS sys.odciNumberList
      7                )
      8          ) lines
      9  ORDER BY id
     10  /
    
            ID TEXT
    ---------- --------------------------------------------------
             1 word1
             1 word2
             1 word3
             2 word4
             2 word5
             2 word6
             3 word7
             3 word8
             3 word9
    
    9 rows selected.
    

    使用XMLTABLE 的替代解决方案:

    SQL> SELECT id,
      2         trim(COLUMN_VALUE) text
      3  FROM t,
      4    xmltable(('"'
      5    || REPLACE(text, ',', '","')
      6    || '"'))
      7  /
    
            ID TEXT
    ---------- ------------------------
             1 word1
             1 word2
             1 word3
             2 word4
             2 word5
             2 word6
             3 word7
             3 word8
             3 word9
    
    9 rows selected.
    
    SQL>
    

    有很多方法可以完成任务,例如MODEL 子句。更多示例见Split comma delimited strings in a table

    【讨论】:

    • 这可以扩展到多列吗?我有一个有 3 列的表,其中 2 列是分开的,我需要将它们转换为多行。 COL1 = a,b,c COL2 = x,y,z 我需要 9 行 a-x , a-y , a-z, b-x, b-y,b-z , x-z,c-y,c-z -- 这可能吗?
    • @NuthanKumar 只是将select的正则表达式中的两列连接起来。
    • 我刚刚遇到了这个答案。我不明白你反对使用prior dbms_random.value() is not null。 (请注意,这里的重点是PRIOR 运算符。)当然,该值永远不会为NULL;这个条件的重点不是提供一种打破递归的方法(其他条件扮演该角色),而是在 Oracle 用来在分层查询中查找循环的伪列中添加一个非重复伪列。如今,PRIOR sys_guid() is not null 的出现频率更高,扮演着完全相同的角色。
    • @mathguy 因为逻辑上它从不为 NULL,所以 sys.odciNumberList 更有意义。
    【解决方案4】:

    不使用 connect by

    WITH CTE AS (SELECT 'a,b,c,d,e' temp,1 slno  FROM DUAL
          UNION 
          SELECT 'f,g',2 from dual
          UNION 
           SELECT 'h',3 FROM DUAL
    )
    ,x as (
      select
      ','||temp||',' temp
      ,slno
      from CTE
    )
    ,iter as (SELECT rownum AS pos
        FROM all_objects
    )
    select
    SUBSTR(x.temp
      ,INSTR(x.temp, ',', 1, iter.pos) + 1
      ,INSTR(x.temp, ',', 1, iter.pos + 1)-INSTR(x.temp, ',', 1, iter.pos)-1
    ) temp
    ,x.slno
    from x, iter
    where iter.pos < = (LENGTH(x.temp) - LENGTH(REPLACE(x.temp, ','))) - 1;
    

    【讨论】:

    • +1 更简单易懂 - 将每个逗号分隔的行与一个足够大的虚拟表连接起来,以包含比每个逗号分隔列表中的值更多的行(所以你得到笛卡尔结果),并使用上面的 substr 或 regexp_substr。很棒。
    【解决方案5】:

    这样试试,

    WITH CTE AS (SELECT 'a,b,c,d,e' temp,1 slno  FROM DUAL
                  UNION 
                  SELECT 'f,g',2 from dual
                  UNION 
                  SELECT 'h',3 FROM DUAL)
         SELECT regexp_substr (temp, '[^,]+', 1, rn)temp, slno
         FROM   cte
         CROSS JOIN 
         (
              SELECT ROWNUM rn
              FROM  (SELECT MAX (LENGTH (regexp_replace (temp, '[^,]+'))) + 1 max_l
                     from cte
                     )
              connect by level <= max_l
             )
         WHERE regexp_substr (temp, '[^,]+', 1, rn) IS NOT NULL
         order by temp;
    

    【讨论】:

      【解决方案6】:

      可以使用下面的查询来转换行中的逗号分隔值

       SELECT trim(x.column_value.extract('e/text()')) COLUMNS
       from t t, table (xmlsequence(xmltype('<e><e>' || replace(valuestring,':','</e><e>')||  
       '</e></e>').extract('e/e'))) x   );  
      

      【讨论】:

        猜你喜欢
        • 2019-09-11
        • 2018-03-26
        • 1970-01-01
        • 2015-10-06
        • 2015-04-01
        • 1970-01-01
        • 2018-11-02
        相关资源
        最近更新 更多