通过SQL查询选择字符串中多个括号之间的值

时间:2015-10-27 14:07:41

标签: sql oracle substring

我正在使用Oracle数据库,我试图在括号中选择值。这是我的表格,其中IDRoads为列。我对此数据库具有只读权限,因此我只能使用SELECT

ID   Roads
--   -----
1    #Chaussée de Waterloo (Ixelles)#
2    #Rue Reper-Vreven (Bruxelles)#
3    #Rue des Fraises (Anderlecht)#
4    #Chaussée de Roodebeek (Woluwe-Saint-Lambert)#
5    #Square Jean Absil (Etterbeek)#Avenue Hansen-Soulie (Etterbeek)#Avenue Le Marinel (Etterbeek)#

基本上,从Roads列,我只想保留括号之间的值。由于最终查询中包含其他表,我想要一个select distinct。所需的输出是:

 ID    Roads
------------------
  1      Ixelles  
  2      Bruxelles 
  3      Anderlecht
  4      Woluwe-Saint-Lambert
  5      Etterbeek, Etterbeek, Etterbeek

我尝试了以下查询,当只有一组括号时它可以正常工作,但是当有多个时(例如ID 5),这不起作用,因为它只返回第一个中的值一组括号:

select distinct substr(roads, instr(roads,'(') + 1, instr(roads,')') - instr(roads,'(') - 1) as roads 
from table

有谁知道我哪里出错?

3 个答案:

答案 0 :(得分:0)

一种解决方案 - 部分基于przemo_pl引用的解决方案:

SELECT SUBSTR( with_parentheses,2,length(with_parentheses)-2) between_parenthesis
FROM 
  (select REGEXP_SUBSTR(dat, '\([^()]*\)+',1,level) AS with_parentheses 
     from (select '#Square Jean Absil (Etterbeek)#Avenue Hansen-Soulie (Anderlecht)#Avenue Le Marinel (Ixelles)#' as dat from dual )
   connect by LEVEL <= ( LENGTH(dat) - LENGTH(REPLACE(dat, '(', '')))    
  )

返回:

between_parenthesis
---------------------
"Etterbeek"
"Anderlecht"
"Ixelles"

如果你想将它重新组装成一排,那就会增加另一种皱纹。

答案 1 :(得分:0)

请参阅我的其他帖子以获得更简单的答案。我要离开这里,因为它仍然是一个有趣的方法和一个关于过度思考解决方案如何过于复杂的教训,有时必须退一步并以不同的方式处理问题。 : - )

好的,你需要循环遍历行和行中的括号:

with tbl(ID, Roads) as (
  select 1, '#Chaussée de Waterloo (Ixelles)#' from dual
  union
  select 2, '#Rue Reper-Vreven (Bruxelles)#' from dual
  union
  select 3, '#Rue des Fraises (Anderlecht)#' from dual
  union
  select 4, '#Chaussée de Roodebeek (Woluwe-Saint-Lambert)#' from dual
  union
  select 5, '#Square Jean Absil (Etterbeek)#Avenue Hansen-Soulie (Etterbeek)#Avenue Le Marinel (Etterbeek)#' from dual
    )
    SELECT ID, Roads,
           COLUMN_VALUE AS match_nbr,
          REGEXP_SUBSTR( Roads ,'\(([^\)]*)\)', 1, COLUMN_VALUE, NULL, 1 ) AS match_value
   FROM   tbl,
          TABLE(
            CAST(
              MULTISET(
                SELECT LEVEL
                FROM   DUAL
                CONNECT BY LEVEL <= REGEXP_COUNT( Roads ,'\(' )
              ) AS SYS.ODCINUMBERLIST
            )
          );

结果:

enter image description here

请参阅here以查找类似帖子,该帖子链接到提供更多信息的其他帖子。我没有声称完全理解它。 : - )

编辑:已更新以使用listagg()获取一行中的道路列表:

SQL> with tbl(ID, Roads) as (
     select 1, '#Chaussée de Waterloo (Ixelles)#' from dual
     union
     select 2, '#Rue Reper-Vreven (Bruxelles)#' from dual
     union
     select 3, '#Rue des Fraises (Anderlecht)#' from dual
     union
     select 4, '#Chaussée de Roodebeek (Woluwe-Saint-Lambert)#' from dual
     union
     select 5, '#Square Jean Absil (Etterbeek)#Avenue Hansen-Soulie (Etterbeek)#Avenue Le Marinel (Etterbeek)#' from dual
       )
       select id,
       listagg(match_value, ', ') within group (order by id) road_list
       from (
       SELECT ID, Roads, COLUMN_VALUE AS match_nbr,
             REGEXP_SUBSTR( Roads ,'\(([^\)]*)\)', 1, COLUMN_VALUE, NULL, 1 ) AS match_value
      FROM   tbl,
             TABLE(
               CAST(
                 MULTISET(
                   SELECT LEVEL
                   FROM   DUAL
                   CONNECT BY LEVEL <= REGEXP_COUNT( Roads ,'\(' )
                 ) AS SYS.ODCINUMBERLIST
               )
             )
       )
       group by id
       order by id
       ;

        ID ROAD_LIST
---------- --------------------------------------------------
         1 Ixelles
         2 Bruxelles
         3 Anderlecht
         4 Woluwe-Saint-Lambert
         5 Etterbeek, Etterbeek, Etterbeek

SQL>

答案 2 :(得分:0)

我将这个作为一个新的答案添加,因为它与我之前的答案有很大的不同,这是一个经典的例子,过度复杂的思维每次迭代都会变得更糟,因此仍然是一个很好的例子!大声笑有时你只需要感觉到你变得太复杂,不要害怕重新开始不同的大头钉!

好的,看看这个。我回到原点,研究了一个模式的字符串。无论是一条道路还是更多道路(实际上,这一列中的多个值的设计违反了基本的数据建模原则,应该重新设计,但是谁不必处理我们无法控制的糟糕设计?),每条道路都被包围磅标志。我的想法是使用正则表达式循环遍历字符串,用括号内的内容替换右括号中的井号符号模式。当然,这会在最后留下一个标志,但我们会在以后清理它。请注意,REGEXP_REPLACE将替换所有出现的模式,如果找到,默认情况下循环遍历所有道路,并且更容易维护,然后嵌套的INSTR(),SUBSTR():

SQL> with tbl(ID, Roads) as (
     select 1, '#Chaussée de Waterloo (Ixelles)#' from dual
     union
     select 2, '#Rue Reper-Vreven (Bruxelles)#' from dual
     union
     select 3, '#Rue des Fraises (Anderlecht)#' from dual
     union
     select 4, '#Chaussée de Roodebeek (Woluwe-Saint-Lambert)#' from dual
     union
     select 5, '#Square Jean Absil (Etterbeek)#Avenue Hansen-Soulie (Etterbeek)#Avenue Le Marinel (Etterbeek)#' from dual
   )
   select ID, rtrim(regexp_replace(Roads, '#.+?\((.+)\)', '\1, '), ', #') Roads
   from tbl;

        ID ROADS
---------- ----------------------------------------
         1 Ixelles
         2 Bruxelles
         3 Anderlecht
         4 Woluwe-Saint-Lambert
         5 Etterbeek, Etterbeek, Etterbeek

SQL>

正则表达式模式解释:

#    Look for a literal pound sign
.    followed by any character
+    followed by one or more of the previous character (any character)
?    make the previous character optional (one or more any characters)
\(   a literal left paren
(    start remembered group 1
.    any character
+    one or more "any" characters
)    end remembered group 1
\)   followed by a literal closing right paren

如果找到上面的字符串,请替换为“replace-with”字符串:

\1       The first remembered group which is what is inside the parentheses.
,<space> followed by a comma and a space

然后它有点快速和肮脏,但只需使用RTRIM删除尾随的逗号空格磅符号。什 - 拉!呼。