我有一个数据集,可以存储几种不同版本的帐号。它可以包含连字符或空格作为段分隔符,也可以完全连接。我想要的输出是前三个和后五个字母数字字符。我在加入这两个细分时遇到问题" 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)
答案 0 :(得分:1)
这是我的尝试。请注意,当regexp_replace()
找不到匹配项时,会返回原始字符串,这就是您无法直接获取空值的原因。我的想法是看结果字符串是否与原始字符串匹配,但当然这对于结果正确且恰好与原始字符串匹配的第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>
答案 1 :(得分: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
答案 2 :(得分:0)
您可以使用REGEXP_REPLACE()
的 position 参数可以取回引用的事实,以便更加接近。在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