强制Oracle Drop全局临时表

时间:2015-09-06 12:10:04

标签: oracle plsql ddl temp-tables

在我们的项目中,我创建了一些全局临时表,如下所示:

CREATE GLOBAL TEMPORARY TABLE v2dtemp (
  id           NUMBER,
  GOOD_TYPE_GROUP       VARCHAR2(250 BYTE),
  GOOD_CODE             VARCHAR2(50 BYTE),
  GOOD_TITLE            VARCHAR2(250 BYTE)
)
ON COMMIT PRESERVE ROWS;

但是当我想删除此表时问题就出现了。 甲骨文不会让我放弃表格,它说:

ORA-14452: attempt to create, alter or drop an index on temporary table already in use

我必须在某些程序中使用此表,但可能会根据其他报告进行更改。所以我应该放弃桌子,然后我应该用我需要的字段重新创建它。

由于某些商业原因,我必须使用它,所以我不可能使用表格或其他东西。我只能使用临时表。 我尝试了提交删除行,但是当我调用我的过程来使用此表中的数据时,表中没有更多行,并且它们已被删除。

任何帮助都会非常感激, 提前谢谢

///编辑

public void saveJSONBatchOpenJobs(final JSONArray array, MtdReport report) {
    dropAndCreateTable();
    String sql = "INSERT INTO v2d_temp " +
            "(ID, KARPARDAZ, GOOD_TYPE_GROUP, GOOD_CODE, GOOD_TITLE, COUNT, "
            + "FACTOR_COUNT, GHABZ_COUNT, DEAL_NO, DEAL_DATE, REQUEST_NO, REQUEST_DATE, "
            + "REQUEST_CLIENT, STATUS, TYPE, MTDREPORT_ID, GEN_SECURITY_DATA_ID) " +
            "VALUES (MTD_KARPARDAZ_OPEN_JOBS_SEQ.nextval,?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";

    getJdbcTemplate().batchUpdate(sql, new BatchPreparedStatementSetter() {

        @Override
        public void setValues(PreparedStatement ps, int i) throws SQLException {
            JSONArray values = array.getJSONArray(i);
            if(!values.get(0).equals("null"))
                ps.setString(1, values.get(0).toString());
            else
                ps.setNull(1, Types.VARCHAR);
            if(!values.get(1).equals("null"))
                ps.setString(2, values.get(1).toString());
            else
                ps.setNull(2, Types.VARCHAR);
            if(!values.get(2).equals("null"))
                ps.setString(3, values.get(2).toString());
            else
                ps.setNull(3, Types.VARCHAR);
            if(!values.get(3).equals("null"))
                ps.setString(4, values.get(3).toString());
            else
                ps.setNull(4, Types.VARCHAR);
            if(!values.get(4).equals("null"))
                ps.setBigDecimal(5, new BigDecimal(values.get(4).toString()));
            else
                ps.setNull(5, Types.NUMERIC);
            if(!values.get(5).equals("null"))
                ps.setBigDecimal(6, new BigDecimal(values.get(5).toString()));
            else
                ps.setNull(6, Types.NUMERIC);
            if(!values.get(6).equals("null"))
                ps.setBigDecimal(7, new BigDecimal(values.get(6).toString()));
            else
                ps.setNull(7, Types.NUMERIC);
            if(!values.get(7).equals("null"))
                ps.setString(8, values.get(7).toString());
            else
                ps.setNull(8, Types.VARCHAR);
            if(!values.get(8).equals("null"))
                ps.setDate(9, new Date(new Timestamp(values.getLong(8)).getDateTime()));
            else
                ps.setNull(9, Types.DATE);
            if(!values.get(9).equals("null"))
                ps.setString(10, values.get(9).toString());
            else
                ps.setNull(10, Types.VARCHAR);
            if(!values.get(10).equals("null"))
                ps.setDate(11, new Date(new Timestamp(values.getLong(8)).getDateTime()));
            else
                ps.setNull(11, Types.DATE);
            if(!values.get(11).equals("null"))
                ps.setString(12, values.get(11).toString());
            else
                ps.setNull(12, Types.VARCHAR);
            if(!values.get(12).equals("null"))
                ps.setString(13, values.get(12).toString());
            else
                ps.setNull(13, Types.VARCHAR);
            if(!values.get(13).equals("null"))
                ps.setString(14, values.get(13).toString());
            else
                ps.setNull(14, Types.VARCHAR);
            if(!values.get(14).equals("null"))
                ps.setLong(15, new Long(values.get(14).toString()));
            else
                ps.setNull(15, Types.NUMERIC);
            if(!values.get(15).equals("null"))
                ps.setLong(16, new Long(values.get(15).toString()));
            else
                ps.setNull(16, Types.NUMERIC);
        }

        @Override
        public int getBatchSize() {
            return array.size();
        }
    });

    String bulkInsert = "declare "
            + "type array is table of d2v_temp%rowtype;"
            + "t1 array;"
            + "begin "
            + "select * bulk collect into t1 from d2v_temp;"
            + "forall i in t1.first..t1.last "
            + "insert into vertical_design values t1(i);"
            + "end;";
    executeSQL(bulkInsert);
}

private void dropAndCreateTable() {
    String dropSql = "declare c int;"
            + "begin "
            + "select count(*) into c from user_tables where table_name = upper('v2d_temp');"
            + "if c = 1 then "
            + "truncate table v2d_temp"
            + "drop table v2d_temp;"
            + " end if;"
            + "end;";
    executeSQL(dropSql);

    String createSql = "CREATE GLOBAL TEMPORARY TABLE v2d_temp (\n"
            + "DEAL_ID               NUMBER,\n"
            + "id           NUMBER,\n"
            + "karpardaz  VARCHAR2(350),\n"
            + "GOOD_TYPE_GROUP       VARCHAR2(250 BYTE),\n"
            + "GOOD_CODE             VARCHAR2(50 BYTE),\n"
            + "GOOD_TITLE            VARCHAR2(250 BYTE),\n"
            + "COUNT                 NUMBER,\n"
            + "FACTOR_COUNT          NUMBER,\n"
            + "GHABZ_COUNT           NUMBER,\n"
            + "DEAL_NO               VARCHAR2(50 BYTE),\n"
            + "DEAL_DATE             DATE,\n"
            + "REQUEST_NO            VARCHAR2(50 BYTE),\n"
            + "REQUEST_DATE          DATE,\n"
            + "REQUEST_CLIENT        VARCHAR2(250 BYTE),\n"
            + "STATUS                VARCHAR2(250 BYTE),\n"
            + "TYPE                  VARCHAR2(250 BYTE),\n"
            + "GEN_SECURITY_DATA_ID  NUMBER(10),\n"
            + "MTDREPORT_ID          NUMBER\n"
            + ")\n"
            + "ON COMMIT PRESERVE ROWS";
    executeSQL(createSql);
}

private void executeSQL(String sql) {
    Connection con = null;
    try {
        con = getConnection();
        Statement st = con.createStatement();
        st.execute(sql);
    } catch (SQLException e) {
        e.printStackTrace();
    } finally {
        if(con != null) {
            try {
                con.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

4 个答案:

答案 0 :(得分:16)

Oracle全局临时表不是临时对象。它们是正确的堆表。我们创建它们一次,任何会话都可以使用它们来存储仅对该会话可见的数据。

临时方面是数据在一个事务或一个会话之外不会持久。关键的实现细节是将数据写入临时表空间而不是永久表空间。但是,数据仍然写入磁盘并从磁盘读取,因此使用全局临时表会产生显着的开销。

重点是我们不应该删除并重新创建临时表。如果您尝试将SQL Server样式逻辑移植到Oracle中,那么您应该考虑使用PL / SQL集合来维护内存中的临时数据。 Find out more.

ORA-14452的具体原因是,如果在会话期间包含数据,我们就不能删除具有会话范围持久性的全局临时表。即使表目前是空的......

SQL> create global temporary table gtt23 (col1 number)
  2  on commit preserve rows
  3  /

Table created.

SQL> insert into gtt23 values (1);

1 row created.

SQL> commit;

Commit complete.

SQL> delete from gtt23;

1 row deleted.

SQL> commit;

Commit complete.

SQL> drop table gtt23;
drop table gtt23
           *
ERROR at line 1:
ORA-14452: attempt to create, alter or drop an index on temporary table already in use

SQL>

解决方案是结束会话并重新连接,或者(有点奇怪)截断表然后删除它。

SQL> truncate table gtt23;

Table truncated.

SQL> drop table gtt23;

Table dropped.

SQL> 

如果某个其他会话正在使用全局临时表 - 这是可能的(因此全局命名法),那么在所有会话断开连接之前,您将无法删除该表。

因此,真正的解决方案是学会正确使用全局临时表:创建特定的全局临时表以匹配每个报告。或者,正如我所说,使用PL / SQL集合。或者,甚至,只要学会编写经过良好调优的SQL。通常我们使用临时表作为一个解决方案写得不好的解决方案,可以使用更好的访问路径保存。

看了你的完整代码后,流程看起来更奇怪了:

  1. 删除并重新创建全局临时表
  2. 填充临时表
  3. 从临时表中选择PL / SQL数组
  4. 使用PL / SQL数组中的批量插入
  5. 插入实际表中

    这里有很多开销和浪费的活动。您需要做的就是将您插入的数据放入v2d_temp并直接填充vertical_design,理想情况下使用INSERT INTO ... SELECT * FROM语句。您需要一些预处理来将JSON数组转换为查询,但这很容易在Java或PL / SQL中实现。

    我觉得全局临时表不适合您的方案。

      

    “我们的老板或其他人坚持通过他们的方式做某事,所以你不能改变它”

    您所拥有的是 Boss问题而不是编程问题。因此,就StackOverflow而言,它是偏离主题的。但无论如何,这里有一些建议。

    要记住的关键是我们不是在谈论对某些次优架构的妥协:你的老板明确提出在多用户环境中无法正常工作。所以,你的选择是:

    1. 忽略ORA-14452错误,继续投入生产,然后使用“但是你告诉我”防守,当这一切都发生了可怕的错误时。这是最弱的一次。
    2. 隐藏垃圾全局表并实现可在多用户场景中运行的内容。这是高风险的,因为如果你破坏了实现,你就没有防御。
    3. 跟你的老板说话。告诉他们你正在遇到ORA-14452错误,说你已经做了一些调查,这似乎是以这种方式使用全局临时表的根本问题,但显然你忽略了一些东西。然后,问他们如何解决这个问题,因为他们之前已经实现了这个问题。这可以有几种方式,也许他们有一个解决方法,也许他们会意识到这是使用全局临时表的错误方式,也许他们会告诉你迷路。无论哪种方式,这都是最好的方法:你已经将关注提升到适当的水平。
    4. 祝你好运。

答案 1 :(得分:4)

另一个值得考虑的方法是重新考虑是否需要临时表。

在从其他RDBMS过渡到Oracle以过度使用它们的人中,这是一种非常常见的编程习惯,因为他们不了解您可以使用公用表表达式等功能隐式实现可以在其中引用的临时结果集。同一查询的其他部分,以及在其他系统上,将数据写入表然后从中进行选择变得很自然。

由于不了解基于PL / SQL的逐行处理在基于SQL的集合处理方面几乎在每个方面都较差,因此失败通常更加复杂 - 代码更慢,更复杂,更容易出错,更容易出错 - - 但Oracle为SQL处理提供了许多其他强大的功能,即使在需要它时,它通常也可以直接集成到SQL SELECT语句中。

作为旁注,在编写用于报告和ETL的Oracle代码的20年中,我只需要使用一次逐行处理,而不需要使用临时表。

答案 2 :(得分:3)

杀死会话是解决ORA-14452错误的唯一方法。使用数据字典查找使用临时表的其他会话并将其终止  使用alter system kill session 'sid,seriall#,instance_id';等语句。

这是Oracle支持文档中提到的“官方”解决方案  如何在临时表(Doc ID 800506.1)的诊断期间诊断ORA-14452。我过去成功地使用过这种方法,原因略有不同。   杀戮会话需要提升特权并且可能很棘手;可能需要多次杀戮,等待和再次尝试。

由于许多原因,这个解决方案几乎肯定是一个坏主意。在实现此操作之前,您应该尝试利用此信息作为证明  做错的方法。例如,“Oracle文档说这种方法需要alter system特权,这是危险的,并引发了一些特权  安全问题......“。

答案 3 :(得分:0)

您可以通过运行来查看所有正在运行的会话:

SELECT * FROM V$SESSION

要杀死某个会话,您有几个选择。以下命令在断开连接之前等待当前的正在进行的事务完成:

ALTER SYSTEM DISCONNECT SESSION ‘sid,serial#’ POST_TRANSACTION

虽然以下命令就像kill -9;它擦除了O / S流程:

ALTER SYSTEM DISCONNECT SESSION ‘sid,serial#’ IMMEDIATE

后者是最有效的杀死会话,阻止您删除临时表。但是,请谨慎使用,因为它是一种相当强大的功能。会话终止后,您可以删除临时表而不会出现错误。

你可以在这里阅读更多关于杀死会话的不同方法(我不隶属于这个网站,当我遇到类似的问题时,我自己也遇到过这个问题): https://chandlerdba.wordpress.com/2013/07/25/killing-a-session-dead/