简体   繁体   English

带有动态查询和表名的 BULK COLLECT/FORALL 语句 - Oracle PL/SQL

[英]BULK COLLECT/FORALL statements with dynamic query and table name- Oracle PL/SQL

I need help in optimizing this query to use bulk collect and forall statements.我需要帮助优化此查询以使用批量收集和 forall 语句。 I have created backup tables (BCK_xxxx) to copy all data from original tables (ORIG_xxx) but I am having problems converting this to bulk collect.我创建了备份表 (BCK_xxxx) 来复制原始表 (ORIG_xxx) 中的所有数据,但是我在将其转换为批量收集时遇到了问题。 Most examples I saw in BULK collect includes already defining the table name and structure using %rowtype.我在 BULK collect 中看到的大多数示例都已经使用 %rowtype 定义了表名和结构。 However, I have hundreds of tables to backup so I need my query specifically the table name to be dynamic.但是,我有数百个表要备份,因此我需要我的查询特别是表名是动态的。 This my original query that inserts/deleted data one by one without bulk collect and takes a lot of time:这是我的原始查询,一一插入/删除数据,无需批量收集,并且需要大量时间:

DECLARE
--select all table names from backup tables (ex: BCK_tablename)
CURSOR cur_temp_tbl IS
    SELECT table_name
    FROM all_tables 
    WHERE OWNER = 'BCKUP'
    ORDER BY 1;  

--select all table names from original tables (ex: ORIG_tablename)
 CURSOR cur_original_tbl IS
    SELECT table_name
    FROM all_tables 
    WHERE OWNER = 'ORIG'
    ORDER BY 1;    
    l_tbl_nm VARCHAR2(30 CHAR);

BEGIN
    --first loop to delete all tables from backup
    FOR a IN cur_temp_tbl LOOP
       l_tbl_nm := a.table_name;                                    
       EXECUTE IMMEDIATE 'DELETE FROM '||  l_tbl_nm;
       l_deleted_cnt :=  l_deleted_cnt +1;            
    END LOOP;

    --second loop to insert data from original to backup        
    FOR b IN cur_original_tbl LOOP            
         l_tbl_nm := b.table_name;   
        CASE
          WHEN INSTR(l_tbl_nm,'ORIG_') > 0 THEN
          l_tbl_nm := REPLACE(l_tbl_nm,'ORIG_','BCK_');
          ELSE
           l_tbl_nm := 'BCK_' || l_tbl_nm;
        END CASE;  

        EXECUTE IMMEDIATE 'INSERT INTO '  || l_tbl_nm || ' SELECT * FROM ' || b.table_name;
        l_inserted_cnt :=  l_inserted_cnt +1;
    END LOOP; 

    dbms_output.put_line('Deleted/truncated tables from backup :' ||l_deleted_cnt);
    dbms_output.put_line('No of tables inserted with data from original to backup :' ||l_inserted_cnt);
EXCEPTION
 WHEN OTHERS THEN
 dbms_output.put_line(SQLERRM);
 dbms_output.put_line(l_tbl_nm);
END;

I am thinking of including the code below to add after my second loop but I am having problems how to declare the 'cur_tbl' cursor and 'l_tbl_data' TABLE data type.我正在考虑在我的第二个循环之后添加下面的代码,但是我在如何声明 'cur_tbl' 游标和 'l_tbl_data' TABLE 数据类型时遇到了问题。 I am unable to use rowtype since the tablename should be dynamic and will change in each iteration of my second loop that will list all table names from original table:我无法使用 rowtype,因为 tablename 应该是动态的,并且会在我的第二个循环的每次迭代中更改,该循环将列出原始表中的所有表名:

TYPE CurTblTyp  IS REF CURSOR;
cur_tbl    CurTblTyp; 
TYPE l_tbl_t IS TABLE OF tablename.%ROWTYPE;
l_tbl_data l_tbl_t ;

OPEN cur_tbl FOR  'SELECT * FROM  :s ' USING b.table_name;
FETCH cur_tbl BULK COLLECT INTO l_tbl_data LIMIT 5000;
EXIT WHEN cur_tbl%NOTFOUND;     
CLOSE cur_tbl;         

FORALL i IN 1 .. l_tbl_data .count
EXECUTE IMMEDIATE 'insert into '||l_tbl_nm||' values (:1)' USING 
l_tbl_data(i);

Hope you can help me and suggest how I can make this code much simpler.希望你能帮助我并建议我如何使这段代码更简单。 Thanks a lot.非常感谢。

It looks like you want to remove all rows from the existing backup tables, then re-copy the entire contents from the original tables to the backup tables.看起来您想从现有备份表中删除所有行,然后将原始表中的全部内容重新复制到备份表中。 If this is correct, using DELETE for the deletion and any loop operation for the insert will be slow.如果这是正确的,使用DELETE进行删除和插入的任何循环操作都会很慢。

First, to remove the data, use TRUNCATE .首先,要删除数据,请使用TRUNCATE Since you are going to repopulate, use the REUSE STORAGE option.由于您要重新填充,请使用REUSE STORAGE选项。 This is the most efficient way to delete all rows from the table.这是从表中删除所有行的最有效方法。

TRUNCATE TABLE <backup table> REUSE STORAGE;

Second, to repopulate, just INSERT with a SELECT .其次,要重新填充,只需使用SELECT INSERT

INSERT INTO <backup table> SELECT * FROM <orig table>;

You can use these in your loops as you loop by table.当您按表循环时,您可以在循环中使用这些。 No need to cursor through the table rows as this will be faster.无需游标通过表行,因为这会更快。

If you have a new table, you can do something similar with a CTAS...如果你有一张新表,你可以用 CTAS 做类似的事情......

CREATE TABLE <backup table> AS SELECT * FROM <orig_table>;

There is a 3rd option in addition to the delete and truncate options: that's rename/drop.除了删除和截断选项外,还有第三个选项:重命名/删除。 You rename the old back up tables, recreate the new backups (CTAS).您重命名旧备份表,重新创建新备份 (CTAS)。 If the create - insert is successful you drop the renamed tables, if the new backup fails you rename the prior old backups back to the initial backup names.如果创建 - 插入成功,则删除重命名的表,如果新备份失败,则将先前的旧备份重命名回初始备份名称。 You basically trade temporary usage of disk space for redo logs.您基本上可以用临时使用的磁盘空间换取重做日志。

You don't need bulk processing, CTAS is still faster than bulk processing.您不需要批量处理,CTAS 仍然比批量处理快。

Have you used FORCE DELETE?你用过强制删除吗? It was first introduced by the Oracle Master JBE it is used to delete the data and ignores the constraint that the table may have and is a lot more faster than other delete statements.它首先由 Oracle Master JBE 引入,用于删除数据并忽略表可能具有的约束,并且比其他删除语句快得多。

FORCE DELETE FROM <table_name>;

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM