将表列转换为键值对

时间:2010-04-13 12:58:36

标签: oracle plsql

我正在编写一个PL / SQL过程,它将Schema A中的一些数据加载到Schema B中。它们都是非常不同的模式,我无法更改模式B的结构。

模式A中的各个表中的列(在视图中连接在一起)需要作为表中2列中的key =>值对插入到模式B中,每个列都在一个单独的行上。例如,员工的名字可能在Schema A中作为employee.firstname出现,但需要在Schema B中输入:

id=>1, key=>'A123', value=>'Smith'

有将近100个密钥,将来可能会增加更多密钥。这意味着我真的不想硬编码任何这些键。

示例代码:

create table schema_a_employees (
    emp_id number(8,0),
    firstname varchar2(50),
    surname varchar2(50)
);
insert into schema_a_employees values ( 1, 'James', 'Smith' );
insert into schema_a_employees values ( 2, 'Fred', 'Jones' );

create table schema_b_values (
    emp_id number(8,0),
    the_key varchar2(5),
    the_value varchar2(200)
);

我认为一个优雅的解决方案很可能涉及一个查找表来确定每个键要插入的值,并且不涉及有效地硬编码几十个类似的语句,如....

insert into schema_b_values ( 1, 'A123', v_firstname );
insert into schema_b_values ( 1, 'B123', v_surname );

我希望能够做的是在Schema A中有一个本地查找表,其中列出了Schema B中的所有键,以及一个列,该列提供了Schema A中表中列的名称用于填充,例如模式B中的密钥“A123”应填充模式A中“firstname”列的值,例如

create table schema_a_lookup (
    the_key varchar2(5),
    the_local_field_name varchar2(50)
);
insert into schema_a_lookup values ( 'A123', 'firstname' );
insert into schema_a_lookup values ( 'B123', 'surname' );

但我不确定如何动态使用查找表中的值来告诉Oracle使用哪些列。

所以我的问题是,是否有一个优雅的解决方案,使用来自schema_a_employees的数据填充schema_b_values表而不对每个可能的密钥(即A123,B123等)进行硬编码?

干杯。

2 个答案:

答案 0 :(得分:1)

我真诚地希望您的架构B不是dreaded key-value pair设计。虽然某些动态属性值表在某些情况下可能很有用,但您会发现除了最基本的查询之外的所有查询几乎都不可能在EAV设计中编写(即使像“找到所有名为John Smith的员工”这样的简单查询也是如此)很难写 - 也无法调整。

无论如何,在您的情况下,您想要编写一个如下所示的动态查询:

SQL> INSERT ALL
  2     INTO schema_b_values VALUES (emp_id, 'A123', firstname)
  3     INTO schema_b_values VALUES (emp_id, 'B123', surname)
  4     SELECT emp_id, firstname, surname
  5       FROM schema_a_employees;

4 rows inserted

您可以使用以下查询生成语句:

SQL> SELECT 'INSERT ALL ' sql_lines FROM dual
  2  UNION ALL
  3  SELECT 'INTO schema_b_values VALUES (emp_id, '''
  4            || dbms_assert.simple_sql_name(the_key)
  5            || ''', '
  6            || dbms_assert.simple_sql_name(the_local_field_name)
  7            ||')'
  8    FROM schema_a_lookup
  9  UNION ALL
 10  SELECT 'SELECT * FROM schema_a_employees' FROM dual;

SQL_LINES
--------------------------------------------------------------------------------
INSERT ALL
INTO schema_b_values VALUES (emp_id, 'A123', firstname)
INTO schema_b_values VALUES (emp_id, 'B123', surname)
SELECT * FROM schema_a_employees

然后,您可以使用EXECUTE IMMEDIATE或DBMS_SQL来执行该语句。

答案 1 :(得分:1)

我喜欢INSERT ALL作为一种方法,因为它提供了一个封装的事务:要么插入所有行,要么都不插入。我对数据迁移的经验是,它往往是一个高度迭代的过程,所以任何有助于清理和回归的东西都是一个明显的恩惠。

SQL> declare
  2      l_src_name varchar2(30) := 'SCHEMA_A_EMPLOYEES';
  3      l_tgt_name varchar2(30) := 'SCHEMA_B_VALUES';
  4      stmt varchar2(32767);
  5  begin
  6      for pk_rec in ( select cc.table_name, cc.column_name
  7                        from user_cons_columns cc
  8                             , user_constraints c
  9                        where c.table_name = l_src_name
 10                        and c.constraint_type = 'P'
 11                        and cc.table_name = l_src_name )
 12      loop
 13          stmt := 'insert all';
 14          for col_rec in ( select * from schema_a_lookup )
 15          loop
 16              stmt := stmt||' into '||l_tgt_name||' values ('
 17                   ||pk_rec.column_name
 18                   ||', '''||col_rec.the_key||''','
 19                   ||col_rec.the_local_field_name
 20                   ||')';
 21          end loop;
 22          stmt := stmt||' select * from '||l_src_name;
 23      end loop;
 24      execute immediate stmt;
 25  end;
 26  /

PL/SQL procedure successfully completed.

SQL> 

多少行?

SQL> select * from schema_b_values;

    EMP_ID THE_K THE_VALUE
---------- ----- ---------------
         1 A123  James
         2 A123  Fred
         1 B123  Smith
         2 B123  Jones

SQL>

我已将查询包装在PL / SQL中,因为它指明了进一步自动化的方法。您可以添加一个表来保存SOURCE和TARGET表名。如果源表具有复合主键,显然可以获得一些乐趣。