REGEXP_SUBSTR 到 return 第一段和最后一段

REGEXP_SUBSTR to return first and last segment

我有一个数据集,它可以以多种不同的形式存储帐号。它可以包含连字符或空格作为段分隔符,也可以完全连接起来。我想要的输出是前三个和最后 5 个字母数字字符。我在加入两个部分时遇到问题“FIRST_THREE_AND_LAST_FIVE:

with testdata as (select '1-23-456-78-90-ABCDE' txt from dual union all
                  select '1 23 456 78 90 ABCDE' txt from dual union all
                  select '1234567890ABCDE' txt from dual union all
                  select '123ABCDE' txt from dual union all
                  select '12DE' txt from dual)
select TXT
       ,regexp_replace(txt, '[^[[:alnum:]]]*',null) NO_HYPHENS_OR_SPACES
       ,regexp_substr(regexp_replace(txt, '[^[[:alnum:]]]*',null), '([[:alnum:]]){3}',1,1) FIRST_THREE
       ,regexp_substr(txt, '([[:alnum:]]){5}$',1,1) LAST_FIVE
       ,regexp_substr(regexp_replace(txt, '[^[[:alnum:]]]*',null), '([[:alnum:]]){3}',1,1) FIRST_THREE_AND_LAST_FIVE
from  testdata;

我想要的输出是:

FIRST_THREE_AND_LAST_FIVE
-------------------------
123ABCDE
123ABCDE
123ABCDE
123ABCDE
(null)

我觉得我遗漏了一些东西,但你不能把你的两个工作列连接起来吗?即,由于前 3 个和后 5 个的正则表达式成功,只需将 FIRST_THREE_AND_LAST_FIVE 替换为:

regexp_substr(regexp_substr(regexp_replace(txt, '[^[[:alnum:]]]*',null), '([[:alnum:]]){3}',1,1)||regexp_substr(txt, '([[:alnum:]]){5}$',1,1),'([[:alnum:]]){5}',1,1)

编辑:在需要时将 regexp_substr 包装器添加到 return null

您可以利用 REGEXP_REPLACE()position 参数这一事实可以采用反向引用来获得更接近的结果。包裹在 CASE 语句中,你得到你想要的:

select case when length(regexp_replace(txt, '[^[:alnum:]]')) >= 8 then
            regexp_replace( regexp_replace(txt, '[^[:alnum:]]')
                          , '^([[:alnum:]]{3}).*([[:alnum:]]{5})$'
                          , '')
       end
  from test_data

这是,所有非字母数字字符替换后的字符串长度大于或等于 8 return 第 1 组和第 2 组,分别是前 3 个和后 8 个 alpha -数字字符。

这感觉……太复杂了。一旦你替换了所有非字母数字字符,你就可以使用普通的 SUBSTR():

with test_data as (
select '1-23-456-78-90-ABCDE' txt from dual union all
select '1 23 456 78 90 ABCDE' txt from dual union all
select '1234567890ABCDE' txt from dual union all
select '123ABCDE' txt from dual union all
select '12DE' txt from dual
       )
, standardised as (
select regexp_replace(txt, '[^[:alnum:]]') as txt
  from test_data
       )
select case when length(txt) >= 8 then substr(txt, 1, 3) || substr(txt, -5) end
  from standardised

这是我的尝试。注意,当 regexp_replace() 没有找到匹配时,原始字符串被 returned,这就是为什么你不能直接得到 null 的原因。我的想法是查看结果字符串是否与原始字符串匹配,但当然这不适用于结果正确且恰好与原始字符串匹配的第 4 行。其他人提到了用 CASE 计算长度等的方法,但我会更严格地检查前 3 个是数字,最后 5 个是 alpha,因为只检查 8 个字符是 returned 并没有保证他们是正确的 8 个字符!我会把它留给 reader.

无论如何,这会查找一个数字,后跟一个可选的破折号或 space(根据规范)并记住该数字(3 次),然后还会记住最后 5 个字母字符。然后 return 按该顺序记住组。

我强烈建议您将此函数设为一个函数,您可以在其中传递字符串并在 return 中获得经过清理的字符串,因为它更易于维护,封装此代码以实现可重用性并允许更好使用 PL/SQL 代码进行错误检查。

SQL> with testdata(txt) as (
  2    select '1-23-456-78-90-ABCDE' from dual
  3    union
  4    select '1 23 456 78 90 ABCDE' from dual
  5    union
  6    select '1234567890ABCDE'      from dual
  7    union
  8    select '123ABCDE'             from dual
  9    union
 10    select '12DE'                 from dual
 11  )
 12  select
 13    case when length(regexp_replace(upper(txt), '^(\d)[- ]?(\d)[- ]?(\d)[- ]?.*([A-Z]{5})$', '')) < 8
 14         -- Needs more robust error checking here
 15         THEN 'NULL'  -- for readability
 16      else regexp_replace(upper(txt), '^(\d)[- ]?(\d)[- ]?(\d)[- ]?.*([A-Z]{5})$', '')
 17    end result
 18  from testdata;

RESULT
--------------------------------------------------------------------------------
123ABCDE
123ABCDE
123ABCDE
123ABCDE
NULL

SQL>