【问题标题】:Oracle regular expression match string from last occurenceOracle 正则表达式匹配最后一次出现的字符串
【发布时间】:2020-12-09 18:31:26
【问题描述】:

我仍在学习 oracle 中的正则表达式并遇到以下错误。下面是我的示例代码

SELECT DISTINCT COALESCE(TO_NUMBER(regexp_substr(USERNAME, '[^.]+', 1, 2)), ID) ID , 
                COALESCE(regexp_substr(USERNAME, '[^.]+', 1, 1), USERNAME) AS USERNAME 
  FROM logs;
ORA-01722: invalid number 
01722. 00000 -  "invalid number"
*Cause:    The specified number was invalid.
*Action:   Specify a valid number.

Table Data

Username                ID
Ravi.1234              1234
Krishna.12345          12345
Ravi.Krishna.1234567   1234567
R.Krishna.987          987
Ravi.K.567890          567890
R.Krish                123
Ravi                   456

Expected Output

ID             Username
1234            Ravi
12345           Krishna
1234567         Ravi.Krishna
987             R.Krishna
567890          Ravi.K

如何重构查询以获得所需的输出。可以使用 substr 代替 regexp 它会提供所需的输出吗?这用于oracle数据库而不是sql。提前致谢。

【问题讨论】:

  • 让我帮你澄清你的问题。所以:在表数据中,你有两个字符串列。用户名由一个或多个用点分隔的“部分”组成。 ID 是一个单一的“字符串”(没有点)——可能完全由数字组成,但这可能很重要,也可能不重要。 (为您澄清!)然后:在查询中,您只想返回用户名的最后“部分”与 ID 匹配的那些行。对于这些行,您希望返回 ID,并单独返回删除 ID 后的用户名部分。这一切都正确吗?
  • 然后:是的,所有这些都可以通过标准字符串函数和条件来完成(避免使用所有正则表达式)。那是你需要的吗?或者您是否也同时利用这个机会来了解更多关于正则表达式的知识?显然,最佳解决方案(不使用正则表达式!)不会帮助您实现最后一个目标,即更多地了解正则表达式。
  • 是的,没有正则表达式,我希望提取数据。谢谢你的帮助。让我试试这个。

标签: sql oracle substr coalesce regexp-substr


【解决方案1】:

如果我正确理解了您的分配(请参阅您的问题下的我的 cmets),您可以使用标准字符串函数和条件执行此操作:

with
  table_data (username, id) as (
    select 'Ravi.1234'           , '1234'    from dual union all
    select 'Krishna.12345'       , '12345'   from dual union all
    select 'Ravi.Krishna.1234567', '1234567' from dual union all
    select 'R.Krishna.987'       , '987'     from dual union all
    select 'Ravi.K.567890'       , '567890'  from dual union all
    select 'R.Krish'             , '123'     from dual union all
    select 'Ravi'                , '456'     from dual
  )
select id, substr(username, 1, instr(username, '.', -1) - 1) as username
from   table_data
where  username like '%.' || id
;

ID      USERNAME            
------- --------------------
1234    Ravi                
12345   Krishna             
1234567 Ravi.Krishna        
987     R.Krishna           
567890  Ravi.K  

WHERE 子句的LIKE 条件中,% 是“任意长度的任意字符串,包括零”的通配符;后面必须跟一个文字 dot,然后是 ID,并且必须是整个 USERNAME 字符串。在select 中,instr(username, '.', -1) 找到username 中“第一个”点的位置,但从末尾数向左移动 - 这就是减号的含义。

带正则表达式功能和条件:

select id, regexp_substr(username, '^(.*)\.' || id || '$', 1, 1, null, 1) as username
from   table_data
where  regexp_like(username, '\.' || id || '$')
;

regexp_substr 的第六个参数表示“括号中的第一个子字符串”(第一个“捕获组”是技术术语)。

【讨论】:

    【解决方案2】:

    我认为REGEXP_REPLACE() 非常适合您的情况,同时过滤掉至少一位数字的值。在当前情况下,您正在尝试将 Username 字符串的第二部分转换为数字,但并非所有这些都是数字,因此会引发错误。此外,您还可以从Username 列中提取ID 列。例如无需在原始表中保留单独的 ID 列。

    因此,考虑使用

    SELECT TO_NUMBER( REGEXP_REPLACE(Username, '[^0-9]+') ) AS ID,
           RTRIM( REGEXP_REPLACE(Username, '[^.]+$'),'.') AS "Username"
      FROM logs
     WHERE REGEXP_LIKE(Username,'[0-9]')
    

    以下选项可以通过使用较少的正则表达式替代上述选项

    SELECT TO_NUMBER( SUBSTR( Username, INSTR(Username, '.',-1)+1, LENGTH( Username ) )) AS ID,
           SUBSTR( Username, 1, INSTR(Username, '.',-1)-1 ) AS "Username"
      FROM logs
     WHERE REGEXP_LIKE(Username,'[0-9]') 
    

    Demo

    【讨论】:

    • 这种方法对数据做了许多隐含的假设(例如,除了 ID 部分的用户名不包含数字等)如果您能说明您需要的所有条件,将会很有帮助为使它起作用 - 显然,在样本数据中都满足条件,但在现实生活数据中可能不一定满足。
    • 再举一个例子:您的解决方案假定应该返回带有 id '456' 的 'Ravi.123'(带有 id 456 和用户名 Ravi)。我的印象是“123”必须匹配“456”,否则该行不应该产生任何输出。
    猜你喜欢
    • 2012-11-17
    • 1970-01-01
    • 2018-06-14
    • 1970-01-01
    • 2016-09-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多