Проблема с Oracle REGEXP_REPLACE при добавлении функции для замены строки, которая использует обратную ссылку - PullRequest
0 голосов
/ 11 января 2019

Мне нужно установить InitCap для набора данных, однако я бы хотел, чтобы несколько выбранных слов были заглавными. Я использовал REGEXP_REPLACE, но столкнулся с проблемой. В моем примере ниже кажется, что Oracle применяет функции к строке замены ДО того, как она разрешит обратные ссылки. На мой взгляд, это плохое решение. В результате (см. Столбец 2) и пишутся с большой буквы, а строка с обратной ссылкой - нет! Обходной путь в столбце 3 также не работает. Другое решение - разбить его на биты как подзапрос, а затем объединить их с функцией UPPER в родительском запросе, что может быть грязно и безобразно. У кого-нибудь есть лучшее / простое решение?

with testdata as (
  select 'MI ROOFING LLC' bizname from dual 
  union all select 'LAKESHORE LLC NILES MI' from dual 
  union all select 'MIDLAND WILLCOX' from dual
)
select bizname, 
  regexp_replace(initcap(bizname),'((^|\W)(Mi|Llc)($|\W))',upper('<x>\1<y>')) bizname1,
  regexp_replace(initcap(bizname),'((^|\W)(Mi|Llc)($|\W))',
  upper(regexp_substr(initcap(bizname),'(^|\W)(Mi|Llc)($|\W)'))) bizname1_workaround
from testdata;

BIZNAME                  BIZNAME1                           BIZNAME1_WORKAROUND 
----------------------  ----------------------------------  -----------------------
MI ROOFING LLC          <X>Mi <Y>Roofing<X> Llc<Y>          MI RoofingMI
LAKESHORE LLC NILES MI  Lakeshore<X> Llc <Y>Niles<X> Mi<Y>  Lakeshore LLC Niles  LLC                                                                                                                   
MIDLAND WILLCOX         Midland Willcox                     Midland Willcox

3 rows selected.

Ответы [ 2 ]

0 голосов
/ 12 января 2019

Я придумал это решение. Я создал встроенную функцию PLSQL для выполнения этой задачи. При необходимости его можно превратить в обычную функцию. Я использовал символ Grave Accent [`] (ASCII 96) в качестве разделителя для разделения битов в списке. Впоследствии я преобразовал список в отдельные строки с помощью regexp_substr. Наконец, я перекомпоновал с ListAgg, применяя функцию InitCap или Upper на основе полученного бита. Символом-разделителем может быть что угодно. Я также ограничил биты до 100, но это может быть выше. Также для списка исключений разделителем должна быть вертикальная линия.

with function InitCapWithAllCapsExceptionList (p varchar2, allCapsList varchar2) return varchar2 as 
retval varchar2(8000);
begin 
  select listagg(case when regexp_like(rslt,'(^|\W)('||allCapsList||')($|\W)','i')
         then upper(rslt) else rslt end ,'') within group (order by lvl) into retval
    from (
      select level lvl, regexp_substr(regexp_replace(initcap(p),'((^|\W)('||allCapsList||')($|\W))','`\1`',1,0,'i'),'[^`]+',1,level) rslt
        from dual connect by level<100)
    where rslt is not null;
   return retval;
end;
testdata as (
  select 'MI ROOFING LLC' bizname from dual 
  union all select 'LAKESHORE LLC NILES MI' from dual 
  union all select 'MIDLAND WILLCOX' from dual
)
 select InitCapWithAllCapsExceptionList(bizname, 'MI|Llc') bizname from testdata;

BIZNAME                                                                         
------------------------
MI Roofing LLC          
Lakeshore LLC Niles MI  
Midland Willcox         

3 rows selected.
0 голосов
/ 11 января 2019

Это не будет самым эффективным, но здесь я разбиваю его на более мелкие шаги. Может быть, поможет другой подход к проблеме. Я сомневаюсь, что вы захотите использовать это для больших наборов данных.

with testdata(id, bizname) as (
-- Base data
  select 1, 'MI ROOFING LLC' from dual union all 
  select 2, 'LAKESHORE LLC NILES MI' from dual union all 
  select 3, 'MIDLAND WILLCOX' from dual
),
parsedata(id, ord, element) as (
-- Treat as a delimited string and parse elements into rows,
--   Initcap()'ing elements not in the "leave-alone" list.
  select id, level as ord, 
  case 
    when regexp_substr(bizname, '(.*?)( |$)', 1, level, null, 1) in ('MI', 'LLC')
      then regexp_substr(bizname, '(.*?)( |$)', 1, level, null, 1)
    else
      initcap(regexp_substr(bizname, '(.*?)( |$)', 1, level, null, 1))
  end as element    
  from testdata
  connect by regexp_substr(bizname, '(.*?)( |$)', 1, level) is not null
    and prior id = id
    and prior sys_guid() is not null
)
--select * from parsedata;
-- Put the rows back together.
SELECT ID, LISTAGG(element, ' ') WITHIN GROUP (ORDER BY ord) bizname
  FROM parsedata
  GROUP BY id
  ORDER BY id;
>
        ID BIZNAME                  
---------- -------------------------
         1 MI Roofing LLC           
         2 Lakeshore LLC Niles MI   
         3 Midland Willcox          

3 rows selected.
...