【问题标题】:Oracle Data Masking using random names from a temp tableOracle Data Masking 使用临时表中的随机名称
【发布时间】:2016-07-07 02:20:33
【问题描述】:

我们需要在 Oracle 10g 数据库中屏蔽一些个人身份信息。我正在使用的过程基于我们用于 Sybase 的另一个屏蔽脚本(工作正常),但由于 Oracle 和 Sybase 数据库中的信息完全不同,我遇到了一些障碍。

该过程是从 PERSON 表中选择所有数据,放入 PERSON_TRANSFER 表中。然后我们使用一个随机数从 PERSON_TRANSFER 表中选择一个随机名称,然后使用该随机名称更新 PERSON 表。这在 Sybase 中运行良好,因为 PERSON 表中每个人只有一行。

我遇到的问题是,在 Oracle DB 中,每个 PERSON 有多个行,并且每行的名称可能不同,也可能不同,例如

|PERSON|
:-----------------:
|PERSON_ID|SURNAME|
|1        |Purple |
|1        |Purple |
|1        |Pink   | <--
|2        |Gray   |
|2        |Blue   | <--
|3        |Black  |
|3        |Black  |

PERSON_TRANSFER 是该表的副本。该表有数百万行,所以我在这里只给出一个非常基本的例子:)

我当前使用的逻辑只是将所有行更新为与该 PERSON_ID 相同,例如

|PERSON|
:-----------------:
|PERSON_ID|SURNAME|
|1        |Brown  |
|1        |Brown  |
|1        |Brown  | <--
|2        |White  |
|2        |White  | <--
|3        |Red    |
|3        |Red    |

但这是不正确的,因为该 PERSON_ID 的不同名称需要以不同方式屏蔽,例如

|PERSON|
:-----------------:
|PERSON_ID|SURNAME|
|1        |Brown  |
|1        |Brown  |
|1        |Yellow | <--
|2        |White  |
|2        |Green  | <--
|3        |Red    |
|3        |Red    |

如何让脚本分别更新不同的名称,而不是仅仅根据 PERSON_ID 更新它们?我的脚本目前看起来像这样

DECLARE
    v_SURNAME    VARCHAR2(30);

BEGIN

    select pt.SURNAME
    into  v_SURNAME
    from   PERSON_TRANSFER pt
    where   pt.PERSON_ID = (SELECT PERSON_ID FROM
                            ( SELECT PERSON_ID FROM PERSON_TRANSFER
                            ORDER BY dbms_random.value )
                            WHERE rownum = 1);
END;

这会导致错误,因为该随机 PERSON_ID 返回的行太多。

1) 是否有更有效的方法来更新 PERSON 表以便随机分配名称? 2) 我如何确保正确屏蔽 PERSON 表,因为对于任何单个 PERSON_ID,各种姓氏都保持不同(或相同,如果它们都相同)?

我希望这是足够的信息。我已经将它简化了一点(该表有更多的列,例如名字、出生日期、TFN 等),希望它使解释更容易。

任何输入/建议/帮助将不胜感激:)

谢谢。

【问题讨论】:

    标签: database oracle random oracle10g data-masking


    【解决方案1】:

    其中一个复杂的问题是,在 PERSON 表中,相同的姓氏可能出现在不同的 person_id 下。您最好使用一个单独的辅助表来保存不同的姓氏(例如,您可以通过从 PERSONS 中选择不同的姓氏来填充它)。

    设置:

    create table persons (person_id, surname) as (
      select 1, 'Purple' from dual union all
      select 1, 'Purple' from dual union all
      select 1, 'Pink'   from dual union all
      select 2, 'Gray'   from dual union all
      select 2, 'Blue'   from dual union all
      select 3, 'Black'  from dual union all
      select 3, 'Black'  from dual
    );
    create table mask_names (person_id, surname) as (
      select 1, 'Apple'  from dual union all
      select 2, 'Banana' from dual union all
      select 3, 'Grape'  from dual union all
      select 4, 'Orange' from dual union all
      select 5, 'Pear'   from dual union all
      select 6, 'Plum'   from dual
    );
    commit;
    

    CTAS 创建 PERSON_TRANSFER:

    create table person_transfer (person_id, surname) as (
    select ranked.person_id, rand.surname
    from   ( select person_id, surname, 
                    dense_rank() over (order by surname) as rk
             from   persons
           ) ranked 
           inner join 
           ( select surname, row_number() over (order by dbms_random.value()) as rnd
             from   mask_names
           ) rand
                  on ranked.rk = rand.rnd
    );
    commit;
    

    结果:

    SQL> select * from person_transfer order by person_id, surname;
    
     PERSON_ID SURNAME
    ---------- -------
             1 Pear
             1 Pear
             1 Plum
             2 Banana
             2 Grape
             3 Apple
             3 Apple
    

    应 OP 的要求添加:范围已扩大 - 现在要求更新原始表中的 surname (PERSONS)。这最好使用merge 语句和我之前演示的连接(子)查询来完成。当PERSONS 表有一个PK 时,这最有效,实际上OP 说现实生活中的表PERSONS 有这样一个PK,由person_id 列和一个附加列date_from 组成。在下面的脚本中,我删除了 persons 并重新创建它以包含此附加列。然后我显示查询和结果。

    注意 - 仍然需要 mask_names 表。一个诱人的替代方案是对persons 中已经存在的姓氏进行洗牌,这样就不需要“帮助”表。唉,这行不通。例如,在一个简单的示例中,persons 只有一行。要混淆姓氏,必须想出不在原始表中的姓氏。更有趣的是,假设每个person_id 恰好有两行,具有不同的姓氏,但这些姓氏在每种情况下都是“约翰”和“玛丽”。只是洗牌这两个名字没有帮助。确实需要像mask_names 这样的“帮助”表。

    新设置

    drop table persons;
    
    create table persons (person_id, date_from, surname) as (
      select 1, date '2016-01-04', 'Purple' from dual union all
      select 1, date '2016-01-20', 'Purple' from dual union all
      select 1, date '2016-03-20', 'Pink'   from dual union all
      select 2, date '2016-01-24', 'Gray'   from dual union all
      select 2, date '2016-03-21', 'Blue'   from dual union all
      select 3, date '2016-04-02', 'Black'  from dual union all
      select 3, date '2016-02-13', 'Black'  from dual
    );
    
    commit;
    
    select * from persons;
    
    
     PERSON_ID DATE_FROM  SURNAME
    ---------- ---------- -------
             1 2016-01-04 Purple
             1 2016-01-20 Purple
             1 2016-03-20 Pink
             2 2016-01-24 Gray
             2 2016-03-21 Blue
             3 2016-04-02 Black
             3 2016-02-13 Black
    
    7 rows selected.
    

    新查询和结果

    merge into persons p
      using (
              select ranked.person_id, ranked.date_from, rand.surname
              from ( 
                     select person_id, date_from, surname, 
                            dense_rank() over (order by surname) as rk
                     from   persons
                   ) ranked 
              inner join ( 
                     select surname, row_number() over (order by dbms_random.value()) as rnd
                     from   mask_names
                   ) rand
              on ranked.rk = rand.rnd
            ) t
      on (p.person_id = t.person_id and p.date_from = t.date_from)
    when matched then update
      set p.surname = t.surname;
    
    commit;
    
    select * from persons;
    
     PERSON_ID DATE_FROM  SURNAME
    ---------- ---------- -------
             1 2016-01-04 Apple
             1 2016-01-20 Apple
             1 2016-03-20 Orange
             2 2016-01-24 Plum
             2 2016-03-21 Grape
             3 2016-04-02 Banana
             3 2016-02-13 Banana
    
    7 rows selected.
    

    【讨论】:

    • 太棒了,谢谢@mathguy!我将如何更新 PERSONS 表,使其与 PERSON_TRANSFER 相同?有没有一种使用行号的安全方法,可能与 person_id 结合使用?
    • 不确定我是否理解问题...我认为 PERSONS 是您的基本(原始)表,应该保留?
    • 原始的 PERSONS 表需要更新,不需要保持原始状态。我们正在从生产恢复到测试,因此作为隐私要求的一部分,需要在 PERSONS 表中屏蔽数据。数据需要有意义,所以我们不能不幸地混淆它。为混乱道歉。
    • 您的 PERSON 表是否(抱歉,我在我的解决方案中错误地将其标记为 PERSONS) - 那么,PERSON 表是否有主键?显然 Person_ID 不是,因为每个 Person_ID 有多行;还有其他一些列是表的 PK 吗?这会让生活更轻松。
    • 不用担心。有一个约束类型 Primary_Key 的约束名称 PD_IX_PD_ID,其中列 column_name=PERSON_ID column_position=1 column_name=DATE_FROM column_position=2。这有帮助吗?
    猜你喜欢
    • 2020-10-31
    • 2013-11-10
    • 1970-01-01
    • 2022-12-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-10-17
    相关资源
    最近更新 更多