通过Oracle SQL查询拆分行中列的逗号分隔值

通过Oracle SQL查询拆分行中列的逗号分隔值,sql,oracle,split,comma,Sql,Oracle,Split,Comma,我有一张如下表: ------------- 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 -------------

我有一张如下表:

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

您可以尝试以下方法:

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;
尝试使用以下查询:

 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

不要使用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
列_值是属于xmltable的伪列。这是快速正确的,允许您在不知道其值的情况下引用列

这将获取列并生成一个值表item、item2、item3,并自动连接到其源表CONTACTS。这在数千行上进行了测试


注意“;”xmltable中是列字段中的分隔符。

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

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;

请参见有多个选项。看

使用REGEXP\u SUBSTR:


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

    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

我尝试了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

这样行吗?从表plit'A,B,C,D,E,F','中选择列_值顺便问一下,您使用的是什么Oracle版本?正如您所看到的,我已经尝试过这样做。我还需要'name'列的对应值。哦,我的意思是,我还需要'ID'列的对应值。拆分是什么?这是你应该写的函数吗?@vipin.huddar真的吗?Oracle11GR2告诉我没有这样的功能。您在Oracle文档中的何处找到此项?我不能通过一个查询来完成此操作,因为我希望避免在所需的DB环境中添加新函数或类似的内容。我还需要“ID”列中的值。@user2531405:您的名称列中预期的最大逗号数是多少?未修复。对于一个特定的ID,可以有任意数量的逗号分隔值。@Nisha这对输入数据中的多个ID不起作用。单行子查询返回多行-我认为您需要在子查询中为ID添加WHERE子句以确定长度。感谢您的帮助,我得到了一些指导。但ID的值是硬编码的,我不会这样做。是的,正如Frank所说,它不适用于多个ID。我一直在关注CONNECT BY方法的性能,只要您知道字符串中元素的最大数量,这种方法就可以完美地工作——您可以通过以下方式进行检查:选择t.name,regexp_countt.name,,“from t group BY t.name have regexp_countt.name,,”>2;
    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
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