具有动态查询和表名的大容量COLLECT/FORALL语句-Oracle PL/SQL
我需要帮助来优化这个查询,以便使用大容量collect和forall语句。我已创建备份表(BCK_xxxx)以复制原始表(ORIG_xxx)中的所有数据,但在将其转换为批量收集时遇到问题。我在BULK collect中看到的大多数示例包括已经使用%rowtype定义了表名和结构。但是,我有数百个表要备份,因此我需要我的查询(特别是表名)是动态的。这是我的原始查询,可以逐个插入/删除数据,而无需批量收集,需要花费大量时间:具有动态查询和表名的大容量COLLECT/FORALL语句-Oracle PL/SQL,sql,oracle,plsql,bulkinsert,forall,Sql,Oracle,Plsql,Bulkinsert,Forall,我需要帮助来优化这个查询,以便使用大容量collect和forall语句。我已创建备份表(BCK_xxxx)以复制原始表(ORIG_xxx)中的所有数据,但在将其转换为批量收集时遇到问题。我在BULK collect中看到的大多数示例包括已经使用%rowtype定义了表名和结构。但是,我有数百个表要备份,因此我需要我的查询(特别是表名)是动态的。这是我的原始查询,可以逐个插入/删除数据,而无需批量收集,需要花费大量时间: DECLARE --select all table names from
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;
我正在考虑在第二个循环之后添加下面的代码,但是我遇到了如何声明'cur_tbl'游标和'l_tbl_data'表数据类型的问题。我无法使用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);
希望您能帮助我,并建议如何使此代码更简单。非常感谢。看起来您想删除现有备份表中的所有行,然后将原始表中的全部内容重新复制到备份表中。如果这是正确的,使用
DELETE
进行删除和插入的任何循环操作都会很慢
首先,要删除数据,请使用TRUNCATE
。由于要重新填充,请使用重用存储
选项。这是从表中删除所有行的最有效方法
TRUNCATE TABLE <backup table> REUSE STORAGE;
您可以在按表循环时在循环中使用这些。无需在表行中使用光标,因为这样会更快
如果你有一个新的表,你可以做一些类似的CTAS
CREATE TABLE <backup table> AS SELECT * FROM <orig_table>;
创建表格作为SELECT*FROM;
看起来您想删除现有备份表中的所有行,然后将原始表中的全部内容重新复制到备份表中。如果这是正确的,使用DELETE
进行删除和插入的任何循环操作都会很慢
首先,要删除数据,请使用TRUNCATE
。由于要重新填充,请使用重用存储
选项。这是从表中删除所有行的最有效方法
TRUNCATE TABLE <backup table> REUSE STORAGE;
您可以在按表循环时在循环中使用这些。无需在表行中使用光标,因为这样会更快
如果你有一个新的表,你可以做一些类似的CTAS
CREATE TABLE <backup table> AS SELECT * FROM <orig_table>;
创建表格作为SELECT*FROM;
除了删除和截断选项外,还有第三个选项:重命名/删除。重命名旧备份表,重新创建新备份(CTA)。如果创建-插入成功,则删除重命名的表;如果新备份失败,则将以前的旧备份重命名回初始备份名称。您基本上可以用磁盘空间的临时使用来换取重做日志
您不需要批量处理,CTA仍然比批量处理快。除了删除和截断选项外,还有第三个选项:重命名/删除。重命名旧备份表,重新创建新备份(CTA)。如果创建-插入成功,则删除重命名的表;如果新备份失败,则将以前的旧备份重命名回初始备份名称。您基本上可以用磁盘空间的临时使用来换取重做日志
您不需要批量处理,CTA仍然比批量处理快。您使用过强制删除吗? 它首先由甲骨文大师J.B.E.介绍 它用于删除数据并忽略表可能具有的约束,并且比其他delete语句快得多
FORCE DELETE FROM <table_name>;
强制删除;
您是否使用了强制删除?
它首先由甲骨文大师J.B.E.介绍
它用于删除数据并忽略表可能具有的约束,并且比其他delete语句快得多
FORCE DELETE FROM <table_name>;
强制删除;
谢谢。。我在创建备份表时已经使用了CTA。我熟悉truncate的用法,但在这里使用它却很犹豫,因为它是一个ddl语句(自动提交)。我还将创建一个类似于此的过程,但它将从原始表中删除数据,并从备份表中插入记录。。在这里使用truncate是相当危险的,除非我检查/验证所有数据都复制到备份表上。还有其他方法可以使用delete吗?或者有没有像truncate这样没有自动提交功能的批量删除功能?您希望使用truncate的原因是您不会在重做日志中充斥所有的删除。是的,您确实需要小心,因为不会从截断回滚。但这些基本上是你的选择,删除和截断。谢谢。。我在创建备份表时已经使用了CTA。我熟悉truncate的用法,但在这里使用它却很犹豫,因为它是一个ddl语句(自动提交)。我还将创建一个类似于此的过程,但它将从原始表中删除数据,并从备份表中插入记录。。在这里使用truncate是相当危险的,除非我检查/验证所有数据都复制到备份表上。还有其他方法可以使用delete吗?或者有没有像truncate这样没有自动提交功能的批量删除功能?您希望使用truncate的原因是您不会在重做日志中充斥所有的删除。是的,您确实需要小心,因为不会从截断回滚。但这些基本上是您的选择,删除和截断。这看起来不像是有效的语法。你能提供一些关于这方面的文档吗?这看起来不像是有效的语法。你能提供有关这方面的文件吗?