【问题标题】:Split comma separated values of a column in row, through Oracle SQL query通过 Oracle SQL 查询拆分行中列的逗号分隔值
【发布时间】:2014-07-02 05:20:54
【问题描述】:

我有一张如下表:

-------------
ID   | NAME
-------------
1001 | A,B,C
1002 | D,E,F
1003 | C,E,G
-------------

我希望这些值显示为:

-------------
ID   | NAME
-------------
1001 | A
1001 | B
1001 | C
1002 | D
1002 | E
1002 | F
1003 | C
1003 | E
1003 | G
-------------

我试过了:

select split('A,B,C,D,E,F', ',') from dual; -- WILL RETURN COLLECTION

select column_value
from table (select split('A,B,C,D,E,F', ',') from dual); -- RETURN COLUMN_VALUE

【问题讨论】:

  • 这行得通吗? SELECT COLUMN_VALUE FROM TABLE(split('A,B,C,D,E,F', ','))顺便问一下你用的是什么Oracle版本?
  • 如您所见,我已经尝试过这样做。我也想要'name'列的相应值。
  • 哦,我的意思是,我也想要'ID'列的对应值。
  • 什么是拆分?这是你应该写的函数吗?
  • @vipin.huddar 真的吗? Oracle 11gR2 告诉我“没有这样的功能”。您在 Oracle 文档中的什么地方找到了这个?

标签: sql oracle split comma


【解决方案1】:

有多种选择。见Split comma delimited strings in a table in Oracle

使用 REGEXP_SUBSTR:

SQL> WITH sample_data AS(
  2  SELECT 10001 ID, 'A,B,C' str FROM dual UNION ALL
  3  SELECT 10002 ID, 'D,E,F' str FROM dual UNION ALL
  4  SELECT 10003 ID, 'C,E,G' str FROM dual
  5  )
  6  -- end of sample_data mimicking real table
  7  SELECT distinct id, trim(regexp_substr(str, '[^,]+', 1, LEVEL)) str
  8  FROM sample_data
  9  CONNECT BY LEVEL <= regexp_count(str, ',')+1
 10  ORDER BY ID
 11  /

        ID STR
---------- -----
     10001 A
     10001 B
     10001 C
     10002 D
     10002 E
     10002 F
     10003 C
     10003 E
     10003 G

9 rows selected.

SQL>

使用 XMLTABLE:

SQL> WITH sample_data AS(
  2  SELECT 10001 ID, 'A,B,C' str FROM dual UNION ALL
  3  SELECT 10002 ID, 'D,E,F' str FROM dual UNION ALL
  4  SELECT 10003 ID, 'C,E,G' str FROM dual
  5  )
  6  -- end of sample_data mimicking real table
  7  SELECT id,
  8        trim(COLUMN_VALUE) str
  9  FROM sample_data,
 10       xmltable(('"'
 11          || REPLACE(str, ',', '","')
 12          || '"'))
 13  /

        ID STR
---------- ---
     10001 A
     10001 B
     10001 C
     10002 D
     10002 E
     10002 F
     10003 C
     10003 E
     10003 G

9 rows selected.

【讨论】:

    【解决方案2】:

    尝试使用以下查询:

     WITH T AS (SELECT 'A,B,C,D,E,F' STR  FROM DUAL)   SELECT    
     REGEXP_SUBSTR (STR, '[^,]+', 1, LEVEL) SPLIT_VALUES  FROM T 
     CONNECT BY LEVEL <= (SELECT LENGTH (REPLACE (STR, ',', NULL)) FROM T)
    

    下面的带有 ID 的查询:

    WITH TAB AS 
    (SELECT '1001' ID, 'A,B,C,D,E,F' STR FROM DUAL
    )
    SELECT    ID, 
    REGEXP_SUBSTR (STR, '[^,]+', 1, LEVEL) SPLIT_VALUES  FROM TAB 
    CONNECT BY LEVEL <= (SELECT LENGTH (REPLACE (STR, ',', NULL)) FROM TAB);
    

    编辑: 尝试对多个 ID 和多个分隔使用以下查询:

    WITH TAB AS 
    (SELECT '1001' ID, 'A,B,C,D,E,F' STR FROM DUAL
    UNION
    SELECT '1002' ID, 'D,E,F' STR FROM DUAL
    UNION
    SELECT '1003' ID, 'C,E,G' STR FROM DUAL
    )
    select id, substr(STR, instr(STR, ',', 1, lvl) + 1, instr(STR, ',', 1, lvl + 1) - instr(STR, ',', 1, lvl) - 1) name 
    from
    ( select ',' || STR || ',' as STR, id from TAB ),
    ( select level as lvl from dual connect by level <= 100 )
    where lvl <= length(STR) - length(replace(STR, ',')) - 1
    
    order by ID, NAME
    

    【讨论】:

    • 我也想要“ID”列中的值。
    • @user2531405:您的姓名列中预期的最大逗号数是多少?
    • 未修复。对于特定 ID,可以有任意数量的逗号分隔值。
    • @Nisha 这不适用于输入数据中的多个 ID(单行子查询返回多行)-我认为您需要在子查询中为 ID 添加 WHERE 子句长度。
    • 感谢您的帮助,我找到了一些方向。但是 ID 的值是硬编码的,我不会这样。是的,正如弗兰克所说,它不适用于多个 ID。
    【解决方案3】:

    我用这种方式解决了类似的问题...

        select YT.ID,
               REPLACE(REGEXP_SUBSTR(','||YT.STR||',',',.*?,',1,lvl.lvl),',','') AS STR
        from YOURTABLE YT
        join (select level as lvl 
              from dual 
              connect by level <= (select max(regexp_count(STR,',')+1) from YOURTABLE)
             ) lvl on lvl.lvl <= regexp_count(YT.STR,',')+1
    

    【讨论】:

      【解决方案4】:

      你可以试试这样的:

      CREATE OR REPLACE TYPE "STR_TABLE"
      as table of varchar2
      
      
      create or replace function GetCollection( iStr varchar2, iSplit char default ',' ) return STR_TABLE as
      pStr varchar2(4000) := trim(iStr);
      rpart varchar(255);
      pColl STR_TABLE := STR_TABLE();
      begin
         while nvl(length(pStr),0) > 0 loop
               pos := inStr(pStr, iSplit );
               if pos > 0 then
                  rpart := substr(pStr,1, pos-1);
                  pStr  := substr(pStr,pos+1,length(pStr));
               else
                  rpart := pStr;
                  pStr := null;
               end if;
               if rpart is not null then
                 pColl.Extend;
                 pColl(pColl.Count) := rpart;
               end if;
         end loop;
         return pColl;
      end;
      

      【讨论】:

      • 我不能通过单个查询来完成,因为我想避免在我所需的数据库环境中添加新功能或类似的东西。
      【解决方案5】:

      不要使用 CONNECT BY 或 REGEXP,这会在复杂查询中产生笛卡尔积。此外,上述解决方案希望您知道可能的结果(A、B、C、D、E、F)而不是组合列表

      使用 XMLTable:

      SELECT c.fname, c.lname,
      trim(COLUMN_VALUE) EMAIL_ADDRESS
       FROM 
        CONTACTS c, CONTACT_STATUS s,
        xmltable(('"'
        || REPLACE(EMAIL_ADDRESS, ';', '","')
        || '"'))
      where  c.status = s.id
      

      COLUMN_VALUE 是属于 xmltable 的伪列。这既快速又正确,允许您在不知道其值的情况下引用列。

      这会获取该列并创建一个值表“item”、“item2”、“item3”,并自动连接到其源表 (CONTACTS)。这已在数千行上进行了测试

      注意';'在 xmltable 中是列字段中的分隔符。

      【讨论】:

        【解决方案6】:

        我尝试了 Lalit Kumar B 的解决方案,到目前为止效果很好。但是有了更多数据,我遇到了性能问题(> 60 行,> 7 级)。因此,我使用了更静态的变体,我想分享一下。

        WITH T AS (
              SELECT 1001 AS ID, 'A,B,C' AS NAME FROM DUAL
        UNION SELECT 1002 AS ID, 'D,E,F' AS NAME FROM DUAL
        UNION SELECT 1003 AS ID, 'C,E,G' AS NAME FROM DUAL
             )   --SELECT * FROM T
        SELECT ID as ID,
               distinct_column AS NAME
          FROM ( SELECT t.ID,       
                        trim(regexp_substr(t.NAME, '[^,]+', 1,1)) AS c1,
                        trim(regexp_substr(t.NAME, '[^,]+', 1,2)) AS c2,
                        trim(regexp_substr(t.NAME, '[^,]+', 1,3)) AS c3,
                        trim(regexp_substr(t.NAME, '[^,]+', 1,4)) AS c4 -- etc.
                   FROM T )
        UNPIVOT ( distinct_column FOR cn IN ( c1, c2, c3, c4 ) )    
        
        
            ID NAME               
        ------ ------
          1001 A                    
          1001 B                    
          1001 C                    
          1002 D                    
          1002 E                    
          1002 F                    
          1003 C                    
          1003 E                    
          1003 G                    
        
        9 Zeilen gewählt
        

        【讨论】:

        • 太棒了。我被CONNECT BY 方法的性能所困扰,只要您知道最大值,这种方法就可以完美地工作。字符串中的元素数量——您可以通过以下方式检查:select t.name, regexp_count(t.name, ',') from t group by t.name having regexp_count(t.name, ',') &gt; 2;
        【解决方案7】:

        此版本也适用于长度超过一个字符的字符串:

        select regexp_substr('A,B,C,Karl-Heinz,D','[^,]+', 1, level) from dual
          connect by regexp_substr('A,B,C,Karl-Heinz,D', '[^,]+', 1, level) is not null;
        

        How to split comma separated string and pass to IN clause of select statement

        【讨论】:

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