繁体   English   中英

如何在Oracle SQL中的varchar2字段中使用X屏蔽字符

[英]How to mask characters with X in a varchar2 field in Oracle SQL

我有一个带有名称的varchar记录列(列)varchar2(30)。 如何使用列出的条件屏蔽名称中的字符。

例如

Tristram Vladimir Chan   <---Original name
1234567890123456789012   <---ruler (character count)
.....XXXXX.....XXXXX.....<--- masking criteria: copy every 5 characters, mask next 5 characters to X
TristXXX XladimXX XXan   <--- expected results

让这个棘手的是对空间“”特征的特殊处理......

什么是实现这一目标的最快捷方式? 可以使用的任何库,程序或快捷方式? 非常感谢你们!

要替换除空白之外的所有内容,您将使用regexp_replace。 如果名称具有最大长度(我假设),则该语句易于编写。 如果没有,那么你需要一个循环,你可以用存储的函数或一些技巧来实现。 以下是最多30个字母的名称声明:

select substr(full_name,1,5) || regexp_replace(substr(full_name,6,5), '[^ ]', 'X') ||
       substr(full_name,11,5) || regexp_replace(substr(full_name,16,5), '[^ ]', 'X') ||
       substr(full_name,21,5) || regexp_replace(substr(full_name,26,5), '[^ ]', 'X') as masked_name
from
(  
  select 'Tristram Vladimir Chan ' as full_name from dual
)

EDIT1:对不起,我忘记了空间管理:这是一个解决方案:

SET serveroutput ON format wraped;
DECLARE
  l_text        varchar2(64) := 'Tristram Vladimir Chan';
  l_mask        VARCHAR2(64) := '.....XXXX..XX.....X.XX';
  l_res         VARCHAR2(64);
  l_text_length INTEGER := length(l_text);
  l_mask_length INTEGER := length(l_mask);
  l_pos         INTEGER := 1;
  l_p           INTEGER := -1;
  l_x           INTEGER := -1;
  l_b           INTEGER := instr(l_text, ' ');
  l_lastb       INTEGER := 0;
  l_cpt         INTEGER := 1;
BEGIN
  -- in case text length doesn't match mask length
  if l_mask_length > l_text_length then
    l_mask := substr(l_mask, 1, l_text_length);
  elsif l_text_length > l_mask_length then
    l_mask := rpad(l_mask, l_text_length, '.');
  end if;
  l_mask_length := l_text_length;
  -- loop for each sequence of characters in the mask
  while l_pos <= l_mask_length loop
    l_p := instr(l_mask, '.'); -- first '.' position
    l_x := instr(l_mask, 'X'); -- first 'X' position
    -- if "." or "X' has not been found
    if l_p = 0 or l_x = 0 then 
      if l_p = 0 then -- no "." found, write 'X' until the end
        l_res := rpad(l_res, l_text_length, 'X');
      elsif l_x = 0 then -- no "X" found, write text until the end
        l_res := l_res || substr(l_text, l_pos, l_text_length);
      end if;
      l_pos := l_mask_length+1;
    else
      if l_p = 1 then -- '.' found, write text until the next 'X'
        l_res := l_res || substr(l_text, l_pos, l_x-1);
        l_mask := substr(l_mask, l_x);
        l_pos := l_pos + l_x-1;
      elsif l_x = 1 then -- 'X' found, write 'X' until the next '.'
        l_res := rpad(nvl(l_res,'X'), nvl(length(l_res),0)+l_p-1, 'X');
        l_mask := substr(l_mask, l_p);
        l_pos := l_pos + l_p-1;
      end if;
    end if;
    -- if a ' ' is found in the original text, replace it in the result
    if l_pos > l_b and l_b > l_lastb then
      l_b := instr(l_text, ' ', 1, l_cpt);
      l_res := substr(l_res, 1, l_b-1) || ' ' || substr(l_res,l_b+1,length(l_res));
      l_lastb := l_b;
      l_cpt := l_cpt + 1;
    end if;
  end loop;
  dbms_output.put_line(l_res);
END;

结果: TristXXX VlXXimir XhXX

有可能改进它,我只是“程序性地”写它。 与其他解决方案相比的优势在于,您可以在此处更改遮罩。

EDIT2 :我重写了代码,它简单得多......:

SET serveroutput ON format wraped;
DECLARE
  l_text        varchar2(64) := 'Tristram Vladimir Chan';
  l_mask        VARCHAR2(64) := '.....XX..XX....XXXX...';
  l_res         VARCHAR2(64);
  l_text_length INTEGER := length(l_text);
  l_mask_length INTEGER := length(l_mask);
  l_pos         INTEGER := 1;
BEGIN
  -- in case text length doesn't match mask length
  if l_mask_length > l_text_length then
    l_mask := substr(l_mask, 1, l_text_length);
  elsif l_text_length > l_mask_length then
    l_mask := rpad(l_mask, l_text_length, '.');
  end if;
  l_mask_length := l_text_length;
  -- loop and build the result string
  while l_pos <= l_mask_length loop
    if substr(l_mask, l_pos, 1) = 'X' then
      l_res := l_res || 'X';
    else
      l_res := l_res || substr(l_text, l_pos, 1);
    end if;
    if substr(l_text, l_pos, 1) = ' ' then
      l_res := substr(l_res, 1, l_pos-1) || ' ';
    end if;
    l_pos := l_pos + 1;
  end loop;
  dbms_output.put_line(l_res);
END;

结果: TristXXm XXadimXX Xhan

我的尝试在这里:

SELECT listagg(dd ,'') within group(ORDER BY rn)
from(
SELECT SPLIT,
     rn, case when mod(rn,10) <=5 and mod(rn,10) >0 then split WHEN split  = ' ' THEN ' ' else 'X' end dd    
FROM
     (SELECT regexp_substr('Tristram Vladimir Chan', '.',LEVEL)split,
          ROWNUM rn
     FROM dual
          CONNECT BY LEVEL <= LENGTH('Tristram Vladimir Chan')
     ));

http://sqlfiddle.com/#!4/d41d8/17810/0

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM