如何从不同的表和视图中删除/插入

Posted

技术标签:

【中文标题】如何从不同的表和视图中删除/插入【英文标题】:How to Delete/Insert from different Tables and Views 【发布时间】:2012-11-08 09:54:53 【问题描述】:

我遇到了一个棘手的问题。在 Oracle 10 中有成对的视图和表,这样的事情会一遍又一遍地完成:

proc_log('DELETE 1');
DELETE FROM table_1;
proc_log('INSERT 1');
INSERT INTO table_1 SELECT * FROM view_1;
proc_log('FINISH 1');

View/Table 2 和 3 和 4 和 5... 和 36 也是如此。

我想做这样的事情:

PROCEDURE proc_import(p_table VARCHAR2) IS
BEGIN
    proc_log('DELETE ' || p_table);
    EXECUTE IMMEDIATE 'DELETE FROM table_' || p_table;
    proc_log('INSERT ' || p_table);
    EXECUTE IMMEDIATE 'INSERT INTO table_' || p_table || ' SELECT * FROM view_' || p_table;
    proc_log('FINISH || p_table);
    COMMIT;
END;

然后调用所有 36 对的函数。

毫不奇怪,这种东西比硬编码的要慢 50%。

我的问题:有没有人知道如何让它更快。或者更好的是,我怎样才能让这些东西与众不同但同样优雅?


编辑

整个东西是这样构建的:

CREATE OR REPLACE PACKAGE PKG_IMPORT IS
  PROCEDURE proc_log IS BEGIN [funky not important stuff] END;
      
  PROCEDURE proc_import IS
  BEGIN
      proc_import_table('1', TRUE);
      proc_import_table('2');
      proc_import_table('3');
      proc_import_table('4', TRUE);
      proc_import_table('5');
      ...
      proc_import_table('36');
  END;

  PROCEDURE proc_import(p_table VARCHAR2, p_whole BOOLEAN DEFAULT FALSE) IS
  BEGIN
    proc_log('DELETE ' || p_table);
    IF p_whole THEN
      EXECUTE IMMEDIATE 'DELETE FROM table_' || p_table;
    ELSE
      EXECUTE IMMEDIATE 'DELETE FROM table_' || p_table || ' WHERE business_logic_applies';
    END IF;
    proc_log('INSERT ' || p_table);
    EXECUTE IMMEDIATE 'INSERT INTO table_' || p_table || ' SELECT * FROM view_' || p_table;
    proc_log('FINISH || p_table);
    COMMIT;
  END;
 END PKG_IMPORT;

过程 proc_import 每晚被作业调用一次。所有 proc_import_table 调用都是硬编码的原因是某些表需要额外的导入信息。

恐怕我不能在这里复制/粘贴原始代码,因为我不知道我是否可以这样做。希望这会有所帮助...

【问题讨论】:

完整地写出来。您仍然需要拨打proc_import 36 次。这不是您问题的答案,但听起来您的数据库结构有问题。 这里似乎不太对劲。如果这是一项夜间工作,并且截断/附加帮助性能,那么我认为运行它需要几秒钟以上的时间。但如果运行时间超过几秒钟,您应该不会注意到使用动态 SQL 几十次的开销所带来的任何差异。也许您应该多运行几次测试,也许是其他一些因素导致了性能差异。 【参考方案1】:

首先,如果您对删除哪些行没有任何条件,您可以截断表格。

TRUNCATE TABLE table_1;

TRUNCATE 是 ddl 操作,它不会物理删除行,它只是操纵高水位线,使得操作非常快。 但也要记住它不能回滚,因为它是 DDL。

另一方面,您可以执行直接路径插入,而不是常规插入。试试这个:

INSERT /*+ append */ into table_1 select * from view_1;

这将导致Oracle将数据直接写入高水位线之上,从而使操作更快。如果您的表格是PARALLEL,它可能也会有所帮助。

【讨论】:

我通常也更喜欢TRUNCATE,但正如你所说,它不是“可回滚的”。如果出现问题,我们不希望有空表。这个直接路径的东西看起来很有趣——我会尝试研究一下它到底做了什么。谢谢。【参考方案2】:

原始代码执行速度更快,因为 Oracle 能够预编译查询,而使用“立即执行”则无法做到这一点。

您的新代码可能看起来更简洁,但实际上更难阅读。至少,您应该给出完整的表名,而不是传入表号,否则其他任何查看您的代码的人都必须弄清楚这些数字的含义。

也就是说,我想我更希望看到 36 个删除语句和 36 个插入语句 - 不那么神秘,而且更整洁。如果它有助于更​​短的 proc,你可以将所有这些语句放入他们自己的 proc 中。

【讨论】:

一般情况下我完全同意你的意见。但在这种情况下,它有点复杂。例如“DELETE/INSERT”操作的顺序很重要。通过使用简短的过程调用,更改和跟踪它要容易得多。此外,我也是一名 Java 开发人员,所以封装功能是我的工作;) 我也是一个 java 开发者——我不会像你那样写它!如果排序很重要,请以正确的顺序编写语句。好的代码设计原则并不意味着你应该把所有东西都浓缩到一个短的空间里,可读性/可维护性同样重要。这是你的代码,但如果我必须维护它,我会不高兴!

以上是关于如何从不同的表和视图中删除/插入的主要内容,如果未能解决你的问题,请参考以下文章

如何使用不同的表和不同的列名连接多个查询

如何使用触发器从两个不同的表中插入数据

加入 3 个表,从 2 个不同的表和按材料名称分组的总数量

如何从不同的表中获取最大版本并加入表?

从不同视图的表中加载 WebView

检索在 sqlite 数据库中插入的所有行,并在包含标签的表视图单元格中显示为具有不同部分的子视图