2015-09-28 5 views
0

У меня есть набор данных, который может хранить номер счета в нескольких разных вариантах. Он может содержать дефисы или пробелы в качестве разделителей сегментов, или он может быть полностью конкатенирован. Мой желаемый результат - это первые три и последние 5 буквенно-цифровых символов. У меня возникли проблемы с соединением двух сегментов «FIRST_THREE_AND_LAST_FIVE:REGEXP_SUBSTR для возврата первого и последнего сегмента

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

В идеале, я хочу, чтобы найти что-то вроде ([[цифру, буква]]) {3} и ([[цифра, буква]]) {5} $, что позволит любому количеству символов в середине , –

ответ

1

Вот моя попытка. Обратите внимание, что когда regexp_replace() не находит совпадения, возвращается исходная строка, поэтому вы не можете получить null напрямую. Моя мысль заключалась в том, чтобы увидеть, соответствует ли строка результата исходной строке, но, конечно, это не будет работать для строки 4, где результат будет правильным и будет соответствовать исходной строке. Другие упомянули методы подсчета длины и т. Д. С CASE, но я бы стал более строгим и проверил, что первые 3 являются числовыми, а последние 5 - альфа, так как только проверка на возвращение 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> 
+0

Мне придется играть с этим, так как это была моя ошибка, потому что я не указывал, что любой из персонажей может быть alnum; но похоже, что я был после. Я попытаюсь устранить этот случай, заменив вашу версию замены на substr {8}, чтобы включить null. Но я полностью согласен с превращением своего окончательного решения в функцию для более широкого использования и обслуживания. –

+0

Спасибо, я смог адаптировать это из-за вашего 'regex_replace'. Я также смог удалить требование 'case', обернув эту функцию с помощью' regexp_substr'. Я считаю этот комбинированный метод более элегантным, потому что он только один раз обрабатывает одно поле данных. Окончательный код: ', regexp_substr (regexp_replace (txt, '^ ([[: alnum:]]) [-]? ([[: Alnum:]]) [-]? ([[: Alnum:]]]) [-]?. * ([[: alnum:]] {5}) $ ',' \ 1 \ 2 \ 3 \ 4 '),' ([[: alnum:]]) {8} ', 1 , 1) FIRST_THREE_AND_LAST_FIVE' –

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) 

EDIT: Добавлена ​​REGEXP_SUBSTR обертку для RET urn null при необходимости

+0

Это технически работает, и я это рассматривал, но он требует двух совершенно отдельных преобразований и объединяет их вместе. –

+0

На самом деле это не работает; потому что последняя запись, которая должна возвращать 'null', возвращает' 12D'. Мне пришлось бы обернуть это в случае и проверить размер строки. –

+0

Нет, он должен возвращать NULL, по крайней мере, для меня ... –

0

Вы можете использовать тот факт, что параметр REGEXP_REPLACE() может возвращать ссылки, чтобы получить намного ближе. Облаченный в сазе вы получите то, что вы после:

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 
+0

Я рассматривал это, а не точно, но аналогичный подход с использованием вложенного FROM, но я предположил, что это будет огромная вычислительная мощность для выполнения двух явно отдельных операций преобразования. –

+0

Это идентично @Paul, вы все равно делаете их. Я только что сделал это для ясности. Здесь есть 3 функции, и только одно из них является регулярным выражением (более тяжелым). Минимум, в который вы собираетесь сделать это, - это два регулярных выражения ... если вы не работаете над десятками миллионов записей, это не имеет значения. TBH это будет относительно быстро на сотнях миллионов ... – Ben

Смежные вопросы