Заменить строку случайным текстом - Oracle SQL - PullRequest
1 голос
/ 23 марта 2019

У меня есть таблица table1 с 1 столбцом - edi_value, которая имеет тип CLOB.

Это записи:

seq  edi_message
1    ISA*00*          *00*          *08*9254110060     *ZZ*123456789      *041216*0805*U*00501*000095071*0*P*>~
    GS*AG*5137624388*123456789*20041216*0805*95071*X*005010~
    ST*824*021390001*005010X186A1~

2    ISA*00*          *00*          *08*56789876678     *ZZ*123456789      *041216*0805*U*00501*000095071*0*P*>~
    GS*AG*5137624388*123456789*20041216*0805*95071*X*005010~
    ST*824*021390001*005010X186A1~

Обращаем ваше внимание - количество строк может варьироваться от 3 до 500.

То, что я ищу, это следующие условия:

  • Игнорировать текст перед первым * в каждой строке, для каждой строки перед первым *, он не должен изменяться. Например GS, ST не должен меняться. ТОЛЬКО после первого * следует рандомизировать
  • Заменить числа [0-9] случайными числами, например, если 0 заменяется на 1, то это должно быть 1 througout.
  • Заменить текст [A-Za-z] случайным текстом, например, если A заменить на W, то его следует заменить на W в течение
  • Оставлять специальные символы как есть

Один символ / число должно отображаться ТОЛЬКО на один случайный символ / число

Вывод может быть:

seq  edi_message
1    ISA*11*          *11*          *13*4030111101     *QQ*102030234      *101010*1313*U*11311*111143121*1*V*>~
    GS*WE*3122000233*102030234*01101010*1313*43121*X*113111~
    ST*300*101241111*113111X130A1~

2    ISA*11*          *11*          *13*30234320023     *QQ*102030234      *101010*1313*U*11311*111143121*1*V*>~
    GS*WE*3122000233*102030234*01101010*1313*43121*X*113111~
    ST*300*101241111*113111X130W1~

Как этого добиться в Oracle SQL?

Ответы [ 2 ]

3 голосов
/ 23 марта 2019

Вы можете использовать translate с вспомогательной функцией для генерации случайных строк (хотя @LukStorms имеет гораздо более точное решение SQL для этого с использованием LISTAGG) вместе сметод токенизации, а затем повторное объединение значений в строки (для демонстрации я использую метод чистого SQL):

create or replace function f(p_low integer, p_high integer) 
    return varchar as
  r varchar(2000) := '';
  x integer;
begin
  for i in p_low..p_high loop
    x := dbms_random.value(0,length(r)+1);
    r := substr(r,1,x)||chr(i)||substr(r,x+1);
  end loop;
  return r;
end;
/
select * from table1;
| EDI_VALUE                                                                                                                                                                                                        |
| :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| ISA*00*          *00*          *08*9254110060     *ZZ*123456789      *041216*0805*U*00501*000095071*0*P*>~<br>    GS*AG*5137624388*123456789*20041216*0805*95071*X*005010~<br>    ST*824*021390001*005010X186A1~ |
| ISA*00*          *00*          *08*56789876678     *ZZ*123456789      *041216*0805*U*00501*000095071*0*P*>~<br>    GS*AG*5137624388*123456789*20041216*0805*95071*X*005010~<br>    ST*824*021390001*005010X186A  |
with t as (select f(48,57)||f(65,90) translate_chars from dual)
select (select new_value
        from (select substr(sys_connect_by_path(r_line,'
'),2) new_value, connect_by_isleaf isleaf
              from (select lvl
                         , substr(line,1,instr(line,'*')-1)||
                             translate(substr(line,instr(line,'*'))
                                      ,'0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'
                                      ,(select translate_chars from t)) r_line
                    from (select level lvl
                               , regexp_substr(edi_value,'^.*$',1,level,'m') line
                          from (select table1.edi_value from dual)
                          connect by level <= regexp_count(edi_value,'^.*$',1,'m')))
              start with lvl=1 connect by lvl=(prior lvl)+1)
        where isleaf=1)
from table1;
| (SELECTNEW_VALUEFROM(SELECTSUBSTR(SYS_CONNECT_BY_PATH(R_LINE,''),2)NEW_VALUE,CONNECT_BY_ISLEAFISLEAFFROM(SELECTLVL,SUBSTR(LINE,1,INSTR(LINE,'*')-1)||TRANSLATE(SUBSTR(LINE,INSTR(LINE,'*')),'0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ',(SELECTTRANSLATE_CHARSFR |
| :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| ISA*66*          *66*          *67*1935006626     *VV*098532471      *650902*6763*K*66360*666613640*6*P*>~<br>    GS*GZ*3084295877*098532471*96650902*6763*13640*I*663606~<br>    ST*795*690816660*663606I072G0~                                            |
| ISA*66*          *66*          *67*32471742247     *VV*098532471      *650902*6763*K*66360*666613640*6*P*>~<br>    GS*GZ*3084295877*098532471*96650902*6763*13640*I*663606~<br>    ST*795*690816660*663606I072G                                             |

db <> скрипка здесь

1 голос
/ 23 марта 2019

Вы можете использовать CTE с CONNECT для генерации строк для букв и цифр.

Затем используйте упорядоченные и зашифрованные строки в переводе.

A CROSS APPLY может использоваться для разбиения сообщения на REGEX.
Затем переводите только те, которые начинаются с *.
И используйте LISTAGG, чтобы склеитьчасти обратно вместе.

WITH 
NUMS as
(
  select 
  LISTAGG(n, '') WITHIN GROUP (ORDER BY n) as n_from,
  LISTAGG(n, '') WITHIN GROUP (ORDER BY DBMS_RANDOM.VALUE) as n_to
  from (select level-1 n from dual connect by level <= 10) 
),
LETTERS as
(
  select 
  LISTAGG(c, '') WITHIN GROUP (ORDER BY c) as c_from,
  LISTAGG(c, '') WITHIN GROUP (ORDER BY DBMS_RANDOM.VALUE) as c_to
  from (select chr(ascii('A')+level-1 ) c from dual connect by level <= 26) 
)
SELECT ca.scrambled as scrambled_message
FROM table1 t
CROSS JOIN NUMS
CROSS JOIN LETTERS
CROSS APPLY 
(
 SELECT LISTAGG(CASE WHEN part like '*%' then translate(part, n_from||c_from, n_to||c_to) else part end, '') WITHIN GROUP (ORDER BY lvl) as scrambled
 FROM
 (
  SELECT 
  level AS lvl,
  REGEXP_SUBSTR(t.edi_message,'[*]\S+|[^*]+',1,level,'m') AS part
  FROM dual
  CONNECT BY level <= regexp_count(t.edi_message, '[*]\S+|[^*]+')+1
 ) parts
) ca;

Тест на db <> fiddle здесь

Пример вывода:

SCRAMBLED_MESSAGE
-----------------------------------------------------------------------------------------------------------
ISA*99*          *99*          *92*3525999959     *PP*950525023      *959595*9292*A*99299*999932909*9*J*>~
    GS*WQ*2900555022*950525023*59959595*9292*32909*I*992999~
    ST*255*959039999*992999I925V9~
ISA*99*          *99*          *92*25023205502     *PP*950525023      *959595*9292*A*99299*999932909*9*J*>~
    GS*WQ*2900555022*950525023*59959595*9292*32909*I*992999~
    ST*255*959039999*992999I925W9~
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...