4

我正在尝试创建一个 PL/SQL 脚本,该脚本从 oracle 生产数据库中提取根“对象”以及所有子对象和其他相关信息。目的是创建一组测试数据来重现生产中遇到的问题。由于数据保护法,数据在提取时需要匿名 - 对象名称、某些类型的 id 和货币金额需要替换。

我试图创建一个或多个临时翻译表,其中包含原始值和匿名版本。然后我会将真实数据与翻译表连接起来,并在需要的地方输出匿名值。

DECLARE
  rootId integer := 123456;

  TYPE anonTableRow IS RECORD 
  (
    id NUMBER,
    fieldC NUMBER,
    anonymizedFieldC NUMBER
  );

  TYPE anonTable IS TABLE OF anonTableRow;
  anonObject anonTable;
BEGIN

  FOR cursor_row IN 
  (
    select 
     id,
     fieldC,
     1234 -- Here I would create anonymized values based on rowNum or something similar
    from 
    prodTable
    where id = rootId
  ) 
  LOOP       
    i := i + 1;
    anonObject(i) := cursor_row; 
  END LOOP;

  FOR cursor_row IN 
  (
    select 
    prod_table.id,
    prod_table.fieldB,
    temp_table.anonymizedFieldC fieldC,
    prod_table.fieldD
    from 
    prod_table
    inner join table(temp_table) on prod_table.id = temp_table.id
    where prod_table.id = 123456789
  ) 
  LOOP       
   dbms_output.put_line('INSERT INTO prod_table VALUES (' || cursor_row.id || ', ' || cursor_row.fieldB || ', ' || cursor_row.fieldC || ', , ' || cursor_row.fieldD);
  END LOOP;
END;
/

然而,我在使用这种方法时遇到了几个问题——似乎几乎不可能将 oracle PL/SQL 表与真正的数据库表连接起来。我对生产数据库的访问受到严格限制,因此我无法创建全局临时表、声明 PL/SQL 之外的类型或任何类似的东西。

我尝试声明自己的 PL/SQL 类型失败,出现了这个问题中提到的问题- 由于权限有限,该解决方案对我不起作用。

是否有一种纯 PL/SQL 方式不需要花哨的权限来实现上述目标?

请注意:上面的代码示例被简化了很多,实际上不需要单独的翻译表——实际上我需要在几个不同的查询中访问原始值和翻译值,所以我不想到处“重新计算”翻译。

4

3 回答 3

1

如果您的数据被正确规范化,那么我想这应该只对内部 ID 是必需的(虽然不确定为什么需要翻译它们)。

以下代码应该适合您,将映射保留在Associative Arrays中:

DECLARE
  TYPE t_number_mapping IS TABLE OF PLS_INTEGER INDEX BY PLS_INTEGER;

  mapping_field_c   t_number_mapping;
BEGIN
  -- Prepare mapping
  FOR cur IN (
    SELECT 101 AS field_c FROM dual UNION ALL SELECT 102 FROM dual -- test-data
  ) LOOP
    mapping_field_c(cur.field_c) := mapping_field_c.COUNT;  -- first entry mapped to 1
  END LOOP;

  -- Use mapping
  FOR cur IN (
    SELECT 101 AS field_c FROM dual UNION ALL SELECT 102 FROM dual -- test-data
  ) LOOP
    -- You can use the mapping when generating the `INSERT` statement
    dbms_output.put_line( cur.field_c || ' mapped to ' || mapping_field_c(cur.field_c) );
  END LOOP;
END;

输出:

101 mapped to 1
102 mapped to 2
于 2016-01-07T10:52:18.143 回答
1

如果这不是一段永久的生产代码,那么“借用”现有的集合类型怎么样 - 例如,您可以访问的 SYS 中的一个定义。

使用架构中的这个脚本,您可以生成一个 SQL Plus 脚本来描述所有 SYS 拥有的类型:

select 'desc ' || type_name from all_types
where typecode = 'COLLECTION'
and owner = 'SYS';

运行生成的脚本将显示您可以访问的所有脚本的结构。这个看起来可能适合例如:

SQL> desc KU$_PARAMVALUES1010
 KU$_PARAMVALUES1010 TABLE OF SYS.KU$_PARAMVALUE1010
 Name                                      Null?    Type
 ----------------------------------------- -------- ----------------------------
 PARAM_NAME                                         VARCHAR2(30)
 PARAM_OP                                           VARCHAR2(30)
 PARAM_TYPE                                         VARCHAR2(30)
 PARAM_LENGTH                                       NUMBER
 PARAM_VALUE_N                                      NUMBER
 PARAM_VALUE_T                                      VARCHAR2(4000)

当然,您不能保证在数据库升级后该类型仍然存在或相同或可供您访问,因此我在开始时需要注意。

于 2016-01-07T10:52:29.917 回答
0

实现这一目标的更通用的方法。在我的示例中,我使用 xquery flwor 表达式和dbms_xmlstore。有关 xquery 的知识是强制性的。

create table mask_user_objects as select * from user_objects where rownum <0;

declare 
   v_s_table varchar2(30) := 'USER_OBJECTS';   --uppercase!!!
   v_d_table varchar2(30) := 'MASK_USER_OBJECTS';  --uppercase!!!
   v_mask_columns xmltype := xmltype('<COLS><OBJECT_NAME>XXXX</OBJECT_NAME>
                                            <DATA_OBJECT_ID>-1</DATA_OBJECT_ID>
                                            <OBJECT_TYPE/>                                            
                                      </COLS>');  --uppercase!!!   
   insCtx DBMS_XMLSTORE.ctxType;
   r NUMBER;
   v_source_table xmltype;
   v_cursor sys_refcursor; 
begin 
   open v_cursor  for 'select * from '||v_s_table||' where rownum <100 ';  
   v_source_table := xmltype(v_cursor);
   close v_cursor; 
   -- Load source table into xmltype. 
   insCtx := DBMS_XMLSTORE.newContext(v_d_table); -- Get saved context
  for rec in ( 
   select tt.column_value from  xmltable('
                  let $col := $anomyze/COLS
                  for $i in  $doc/ROWSET/ROW           
                   let $row := $i  
                   return <ROWSET>             
                            <ROW>                                         
                                {                                                                
                                 for $x in $row/* 
                                  return if(
                                       exists($col/*[name() = $x/name()] )                                                                                             
                                  ) then element{$x/name()}{ $col/*[name() = $x/name()]/text() }  
                                    else element{$x/name()}{$x/text()}                   
                                }                   
                            </ROW>             
                         </ROWSET>
                  '
                  passing v_source_table as "doc"
                       ,  v_mask_columns as "anomyze"
                  ) tt) loop
                  null;
             r := DBMS_XMLSTORE.insertXML(insCtx,  rec.column_value);
end loop;
 DBMS_XMLSTORE.closeContext(insCtx);      

end; 
于 2016-01-07T14:35:34.777 回答