【问题标题】:Replace string with random text - Oracle SQL用随机文本替换字符串 - Oracle SQL
【发布时间】:2019-08-14 09:04:23
【问题描述】:

我有一个表 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 中如何实现?

【问题讨论】:

    标签: sql oracle


    【解决方案1】:

    您可以将translate 与用于生成随机字符串的辅助函数一起使用(尽管@LukStorms 有一个使用LISTAGGmuch neater SQL solution for that),以及一种标记化然后将值重新连接成行的方法(我使用纯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*>~
    GS*AG*5137624388*123456789*20041216*0805*95071*X *005010~
    ST*824*021390001*005010X186A1~ | | 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*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*>~
    GS*GZ*3084295877*098532471*96650902*6763*13640*I *663606~
    ST*795*690816660*663606I072G0~ | | ISA*66* *66* *67*32471742247 *VV*098532471 *650902*6763*K*66360*666613640*6*P*>~
    GS*GZ*3084295877*098532471*96650902*6763*13640*I *663606~
    ST*795*690816660*663606I072G |

    db小提琴here

    【讨论】:

    • 这很好用!如果我想更改它以使其不会更改每次更改的两 (2) * 的值,我应该更改哪个代码?例如。 ST*824* 应该保持原样,只有下一部分改变?
    • instr 有两个可选参数,positionoccurrence 用于这类事情,所以这是一个小调整:dbfiddle.uk/…
    • 你在使用 Skype 吗?
    【解决方案2】:

    您可以使用 CTE 和 CONNECT 来生成字母和数字的字符串。

    然后在翻译中使用有序和加扰的字符串。

    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;
    

    dbfiddle here

    的测试

    示例输出:

    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~
    

    【讨论】:

    • 非常巧妙的生成翻译字符串的方式。您可以将它们连接起来而不是嵌套translates,不是吗?
    • 这样我的意思是:dbfiddle.uk/…
    • 天哪,你对连接它们提出了一个很好的观点。 (为什么我没有看到明显的!)谢谢。
    • 它改变了同一行的第 2 行和第 3 行。我的意思是 GS,ST 应该保持原样而不是改变。
    • 哦,就是这样。不仅是 varchar 的开头,而且在字符串中的换行符之后。嗯,这会有点难做。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-03-08
    • 2017-05-20
    • 1970-01-01
    • 2015-01-20
    • 2018-05-21
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多