0

我有一个数据集,它可以存储几个不同变体的帐号。它可能包含连字符或空格作为段分隔符,也可能是完全连接的。我想要的输出是前三个和后五个字母数字字符。我在加入“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)
4

3 回答 3

1

这是我的尝试。请注意,当regexp_replace()找不到匹配项时,将返回原始字符串,这就是您无法直接获取 null 的原因。我的想法是查看结果字符串是否与原始字符串匹配,但当然这不适用于结果正确且恰好与原始字符串匹配的第 4 行。其他人用 CASE 提到了计算长度等的方法,但我会更加严格并检查前 3 个是数字,后 5 个也是 alpha,因为仅检查返回的 8 个字符并不能保证它们是正确的8个字!我会把它留给读者。

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

我强烈建议您将其设为一个函数,在其中传递您的字符串并获得一个干净的字符串作为回报,因为它将更容易维护,封装此代码以实现可重用性,并允许使用 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})$', '\1\2\3\4')) < 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})$', '\1\2\3\4')
 17    end result
 18  from testdata;

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

SQL>
于 2015-09-28T22:27:10.263 回答
0

我觉得我错过了一些东西,但你不能把你的两个工作列连接起来吗?即,由于您对前 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 包装器以在需要时返回 null

于 2015-09-28T16:08:32.023 回答
0

您可以使用position参数REGEXP_REPLACE()可以使用反向引用来更接近这一事实。包裹在 CASE 语句中,您将得到您所追求的:

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

也就是说,如果替换所有非字母数字字符的字符串的长度大于或等于 8,则返回第 1 组和第 2 组,它们分别是前 3 个和后 8 个字母数字字符。

这感觉……过于复杂。替换所有非字母数字字符后,您可以使用普通的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
于 2015-09-28T16:23:27.700 回答