从包含依赖行的DB中提取行

时间:2009-06-22 15:54:03

标签: sql database oracle testing export

我想为我的Oracle数据库中的一行生成插入字符串,包括其他表中的所有相关行(及其相关行)。

示例:

CREATE TABLE a (
  a_id number PRIMARY KEY,
  name varchar2(100)
);
CREATE TABLE b (
  b_id number PRIMARY KEY,
  a_id number REFERENCES a(a_id)
);

当我从a_id = 1中提取行时,结果应该是该行和相关行的插入字符串:

INSERT INTO a(a_id, name) VALUES (1, 'foo');
INSERT INTO b(b_id, a_id) VALUES (1, 1);
INSERT INTO b(b_id, a_id) VALUES (2, 1);
INSERT INTO b(b_id, a_id) VALUES (3, 1);

我想这样做的原因是,我有大型数据库,在那之间有许多不同的表和约束,我想提取一小部分数据作为测试数据。

3 个答案:

答案 0 :(得分:12)

可能有一些工具已经完成,但是从起始表中任意提取所有行表本身就是一个小的开发任务。我不能为你写完整件事,但我可以帮你开始 - 我开始写它,但是大约20分钟后,我意识到这是一项更多的工作,我想承诺一个无偿的答案。

我可以看到通过使用dbms_ouput和user_cons_columns&的递归PL / SQL过程最好地完成它。 user_constraints为源表创建insert语句。您可以通过编写所有插入来作弊,就好像列是char值一样,因为Oracle将隐式地将任何char值转换为正确的数据类型,假设您的NLS参数在源和列表上是相同的。目标系统。

请注意,如果您的表中存在循环关系,则下面的包会出现问题;此外,在早期版本的Oracle上,使用dbms_output可能会耗尽缓冲区空间。这两个问题都可以通过将生成的sql插入到sql上具有唯一索引的登台表中来解决,如果遇到唯一的密钥冲突则中止递归。下面的大节省时间是MakeParamList函数,它将返回列列表的游标转换为以逗号分隔的列表,或者将单个表达式转换为以逗号分隔的形式显示这些列的值(当作为在针对表的查询中选择select子句。

另请注意,在您进一步修改之后,以下包将无法正常工作(我停止编写它的原因之一):生成的初始insert语句基于传入的constraint_vals参数将导致a的假设生成单行 - 当然,一旦开始递归,几乎肯定不是这种情况(因为父级会有很多子行)。您需要将第一个语句(以及后续递归调用)的生成更改为循环内部,以处理对第一个EXECUTE IMMEDIATE调用的调用生成多行而不是单个行的情况。让它工作的基础是在这里,你只需要研究细节并使外部光标工作。

最后还要注意:您不太可能运行此过程来生成一组行,这些行在插入目标系统时会产生“干净”的数据集,因为尽管您将获得所有依赖数据,该数据可能依赖于您未导入的其他表(例如,您遇到的第一个子表可能有其他外键指向与您的初始表无关的表)。在这种情况下,您可能希望从详细信息表开始,而不是向下工作;这样做,你也想要反转你生成的语句的顺序,或者使用脚本实用程序,或者像上面提到的那样将sql插入到临时表中,使用序列,然后用降序排序选择它

至于调用它,你传递逗号分隔的列列表以约束为constraint_cols,并将相应的逗号分隔值列表作为constraint_vals,例如:

exec Data_extractor.MakeInserts ('MYTABLE', 'COL1, COL2', '99, 105')

这是:

CREATE OR REPLACE PACKAGE data_extractor
IS
   TYPE column_info IS RECORD(
      column_name   user_tab_columns.column_name%TYPE
   );

   TYPE column_info_cursor IS REF CURSOR
      RETURN column_info;

   FUNCTION makeparamlist(
      column_info   column_info_cursor
    , get_values    NUMBER
   )
      RETURN VARCHAR2;

   PROCEDURE makeinserts(
      source_table      VARCHAR2
    , constraint_cols   VARCHAR2
    , constraint_vals   VARCHAR2
   );
END data_extractor;


CREATE OR REPLACE PACKAGE BODY data_extractor
AS
   FUNCTION makeparamlist(
      column_info   column_info_cursor
    , get_values    NUMBER
   )
      RETURN VARCHAR2
   AS
   BEGIN
      DECLARE
         column_name   user_tab_columns.column_name%TYPE;
         tempsql       VARCHAR2(4000);
         separator     VARCHAR2(20);
      BEGIN
         IF get_values = 1
         THEN
            separator := ''''''''' || ';
         ELSE
            separator := '';
         END IF;

         LOOP
            FETCH column_info
             INTO column_name;

            EXIT WHEN column_info%NOTFOUND;
            tempsql := tempsql || separator || column_name;

            IF get_values = 1
            THEN
               separator := ' || '''''', '''''' || ';
            ELSE
               separator := ', ';
            END IF;
         END LOOP;

         IF get_values = 1
         THEN
            tempsql := tempsql || ' || ''''''''';
         END IF;

         RETURN tempsql;
      END;
   END;

   PROCEDURE makeinserts(
      source_table      VARCHAR2
    , constraint_cols   VARCHAR2
    , constraint_vals   VARCHAR2
   )
   AS
   BEGIN
      DECLARE
         basesql               VARCHAR2(4000);
         extractsql            VARCHAR2(4000);
         tempsql               VARCHAR2(4000);
         valuelist             VARCHAR2(4000);
         childconstraint_vals  VARCHAR2(4000);
      BEGIN
         SELECT makeparamlist(CURSOR(SELECT column_name
                                       FROM user_tab_columns
                                      WHERE table_name = source_table), 0)
           INTO tempsql
           FROM DUAL;

         basesql := 'INSERT INTO ' || source_table || '(' || tempsql || ') VALUES (';

         SELECT makeparamlist(CURSOR(SELECT column_name
                                       FROM user_tab_columns
                                      WHERE table_name = source_table), 1)
           INTO tempsql
           FROM DUAL;

         extractsql := 'SELECT ' || tempsql || ' FROM ' || source_table 
                       || ' WHERE (' || constraint_cols || ') = (SELECT ' 
                       || constraint_vals || ' FROM DUAL)';

         EXECUTE IMMEDIATE extractsql
                      INTO valuelist;

         -- This prints out the insert statement for the root row
         DBMS_OUTPUT.put_line(basesql || valuelist || ');');

         -- Now we construct the constraint_vals parameter for subsequent calls:
         SELECT makeparamlist(CURSOR(  SELECT column_name
                                         FROM user_cons_columns ucc
                                            , user_constraints uc
                                        WHERE uc.table_name = source_table
                                          AND ucc.constraint_name = uc.constraint_name
                                     ORDER BY position)
                             , 1)
           INTO tempsql
           FROM DUAL;

         extractsql := 'SELECT ' || tempsql || ' FROM ' || source_table 
                       || ' WHERE ' || constraint_cols || ' = ' || constraint_vals;

         EXECUTE IMMEDIATE extractsql
                      INTO childconstraint_vals;

         childconstraint_vals := childconstraint_vals;

-- Now iterate over the dependent tables for this table
-- Cursor on this statement:
--    SELECT uc.table_name child_table, uc.constraint_name fk_name
--      FROM user_constraints uc
--         , user_constraints ucp
--     WHERE ucp.table_name = source_table
--      AND uc.r_constraint_name = ucp.constraint_name;

         --   For each table in that statement, find the foreign key 
         --   columns that correspond to the rows
         --   in the parent table
         --  SELECT column_name
         --    FROM user_cons_columns
         --   WHERE constraint_name = fk_name
         --ORDER BY POSITION;

         -- Pass that columns into makeparamlist above to create 
         -- the constraint_cols argument of the call below:

         -- makeinserts(child_table, ChildConstraint_cols, childconstrain_vals);
      END;
   END;
END data_extractor;

答案 1 :(得分:1)

我只使用普通的旧SQL来执行这些任务 - 使用select语句生成插入内容:

set pagesize 0
set verify off

  SELECT 'INSERT INTO a(a_id, name) VALUES ('
         || a_id || ', '
         || '''' || name || ''');'
    FROM a
   WHERE a_id = &&1;

  SELECT 'INSERT INTO b(b_id, a_id) VALUES ('
         || b_id || ', '
         || a_id || ');'
    FROM b
   WHERE a_id = &&1;

答案 2 :(得分:0)

我认为DBUnit可以做到这一点。