在Oracle中拆分逗号分隔的字符串无法正常工作

时间:2017-01-31 15:45:35

标签: sql oracle

我想从逗号分隔的字符串中检索一组值到查询中的IN子句。有人知道为什么以下代码无法正常工作;

WITH xtable AS (
         SELECT 1 ID, '116,117,169,170,173,175,9015,44008,44367,44446,45081,45083,46779,47161,47222' AGT FROM DUAL
         UNION ALL
         SELECT 2 ID, '456,789' AGT FROM DUAL
         UNION ALL
         SELECT 3 ID, '116,117,169,170,173,175,9015,44008,44367,44446,45081,45083,46779,47161' AGT FROM DUAL
     )      
  select regexp_substr(x.AGT,'[^,]+', 1, level)
  from xtable x
  where x.ID = 3
  connect by regexp_substr(x.AGT, '[^,]+', 1, level) is not null;

在这种情况下,结果应为

    AGT
1   116
2   117
3   169
4   170
5   173
6   175
7   9015
8   44008
9   44367
10  44446
11  45081
12  45083
13  46779
14  47161

相反,我得到了几乎无限循环的相同值

3 个答案:

答案 0 :(得分:2)

您的查询中的问题是where子句将仅应用于级别1而不是任何进一步。

使用嵌套表尝试此操作:

WITH xtable AS (
         SELECT 1 ID, '116,117,169,170,173,175,9015,44008,44367,44446,45081,45083,46779,47161,47222' AGT FROM DUAL
         UNION ALL
         SELECT 2 ID, '456,789' AGT FROM DUAL
         UNION ALL
         SELECT 3 ID, '116,117,169,170,173,175,9015,44008,44367,44446,45081,45083,46779,47161' AGT FROM DUAL
     )      
  select regexp_substr(x.AGT,'[^,]+', 1, t.column_value) agt
  from xtable x cross join table(
    cast(
      multiset(
        select level
        from dual
        connect by level <= regexp_count(x.AGT,',') + 1
      )as sys.odcinumberlist
    )
  ) t
  where x.id = 3;

这是一个通用查询,如果你想一次转换所有这些,你甚至可以使用where子句。

在Oracle 12c +中,您可以使用OUTER APPLY来实现相同的效果和更简单的语法:

WITH xtable AS (
         SELECT 1 ID, '116,117,169,170,173,175,9015,44008,44367,44446,45081,45083,46779,47161,47222' AGT FROM DUAL
         UNION ALL
         SELECT 2 ID, '456,789' AGT FROM DUAL
         UNION ALL
         SELECT 3 ID, '116,117,169,170,173,175,9015,44008,44367,44446,45081,45083,46779,47161' AGT FROM DUAL
     )      
  select regexp_substr(x.AGT,'[^,]+', 1, t.n) agt
  from xtable x
  outer apply (
        select level n
        from dual
        connect by level <= regexp_count(x.AGT,',') + 1
  ) t
  where x.id = 3; 

答案 1 :(得分:1)

您在多行中拥有相同的值,因此您的连接在它们之间反弹。分层查询有点棘手,涉及多行。如果您真的只想要单个ID的值,则可以在子查询中进行过滤:

WITH xtable AS (
         SELECT 1 ID, '116,117,169,170,173,175,9015,44008,44367,44446,45081,45083,46779,47161,47222' AGT FROM DUAL
         UNION ALL
         SELECT 2 ID, '456,789' AGT FROM DUAL
         UNION ALL
         SELECT 3 ID, '116,117,169,170,173,175,9015,44008,44367,44446,45081,45083,46779,47161' AGT FROM DUAL
     )      
  select regexp_substr(x.AGT,'[^,]+', 1, level)
  from (select AGT from xtable where ID = 3) x
  connect by regexp_substr(x.AGT, '[^,]+', 1, level) is not null;

REGEXP_SUBSTR(X.AGT,'[^,]+',1,LEVEL)                                        
----------------------------------------------------------------------------
116
117
169
170
173
175
9015
44008
44367
44446
45081
45083
46779
47161

14 rows selected. 

或者您可以包含id = prior id,但是还需要使用非确定性函数调用来阻止循环:

WITH ... (...)
  select regexp_substr(x.AGT,'[^,]+', 1, level)
  from xtable x
  where x.ID = 3
  connect by id = prior id and prior dbms_random.value is not null
  and regexp_substr(x.AGT, '[^,]+', 1, level) is not null;

返回相同的14行,如果包含多个ID,则可以正常工作。

答案 2 :(得分:1)

Splitting Delimited Strings - SO Oracle Documentation page中有多种解决方案。

使用相关分层查询的解决方案是:

WITH xtable (id, agt ) AS (
  SELECT 1, '116,117,169,170,173,175,9015,44008,44367,44446,45081,45083,46779,47161,47222' FROM DUAL
UNION ALL
  SELECT 2, '456,789' FROM DUAL
UNION ALL
  SELECT 3, '116,117,169,170,173,175,9015,44008,44367,44446,45081,45083,46779,47161' FROM DUAL
)
SELECT id,
       REGEXP_SUBSTR( x.agt, '[^,]+', 1, t.COLUMN_VALUE ) AS item
FROM   xtable x
       CROSS JOIN
       TABLE(
         CAST(
           MULTISET(
             SELECT LEVEL
             FROM   DUAL
             CONNECT BY LEVEL <= REGEXP_COUNT( x.agt, '[^,]+' )
           )
         ) AS SYS.ODCINUMBERLIST
       ) t
WHERE  x.id = 3;

<强>输出

ID  ITEM
-- -----
 3   116
 3   117
 3   169
 3   170
 3   173
 3   175
 3  9015
 3 44008
 3 44367
 3 44446
 3 45081
 3 45083
 3 46779
 3 47161