1

我有一个带有名称的 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

让这件事变得棘手的是对空格“”字符的特殊处理......

完成此任务的最快方法是什么?任何可以使用的库、过程或快捷方式?非常感谢各位!!!!

4

3 回答 3

3

要替换除空白之外的所有内容,您将使用 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
)
于 2013-09-27T10:25:02.303 回答
1

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

于 2013-09-27T10:51:01.220 回答
1

我的尝试在这里:

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

于 2013-09-27T11:53:49.867 回答