强制 Oracle 删除全局温度 Table

Force Oracle Drop Global Temp Table

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

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;

但是当我想删除这个 table 时问题就来了。 Oracle 不允许我删除 table,它说:

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

我必须在某些过程中使用这个 table 但它可能会根据其他报告进行更改。所以我应该总是删除 table 然后我应该用我需要的字段重新创建它。

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

任何帮助将不胜感激, 提前致谢

/// 编辑

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();
            }
        }
    }
}

Oracle 全局临时 table 不是临时对象。它们是适当的堆 tables。我们创建它们一次,任何会话 都可以使用它们来存储仅对 那个会话.

可见的数据

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

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

ORA-14452 的具体原因是我们无法删除具有会话范围持久性的全局临时 table 如果它在会话期间包含数据。即使 table 当前为空...

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>

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

SQL> truncate table gtt23;

Table truncated.

SQL> drop table gtt23;

Table dropped.

SQL> 

如果其他一些会话正在使用全局临时 table - 这是可能的(因此 global 命名法)那么您将无法删除table 直到所有会话断开连接。

所以真正的解决办法是学会正确使用全局临时 tables:创建特定的全局临时 tables 来匹配每个报告。或者,正如我所说,改用 PL/SQL 集合。或者,甚至,只是学习写得很好 SQL。我们经常使用临时 tables 来解决写得不好的查询,可以用更好的访问路径保存它。


看了你的完整代码,流程似乎更奇怪了:

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

这里有太多的开销和浪费 activity。您需要做的就是获取您插入 v2d_temp 的数据并直接填充 vertical_design,最好使用 INSERT INTO ... SELECT * FROM 语句。您需要进行一些预处理才能将 JSON 数组转换为查询,但这在 Java 或 PL/SQL.

中很容易实现

在我看来,全局临时 table 不是适合您的方案的正确解决方案。


"our boss or other persons persist to do something through their way, so you cannot change that"

您遇到的是 Boss 问题,而不是 编程问题。因此,就 Whosebug 而言,它是题外话。但这里还是有一些建议。

要记住的关键是我们不是在谈论对某些次优架构的妥协:你老板明确提出的建议在多用户中行不通环境。所以,您的选择是:

  1. 忽略 ORA-14452 错误,继续生产,然后在出现严重错误时使用 "but you told me to" 防御。这是最弱的玩法。
  2. 隐蔽地垃圾全局 tables 并实现一些可以在多用户场景中工作的东西。这是高风险的,因为如果你搞砸了实施,你就没有防御能力。
  3. 和你的老板谈谈。告诉他们你 运行 进入了 ORA-14452 错误,说你已经做了一些调查并且以这种方式使用全局临时 tables 似乎是一个基本问题,但显然你已经忽略了一些东西。然后,询问他们以前实施过这个问题是如何解决的。这可以有多种方式,也许他们有解决方法,也许他们会意识到这是使用全局临时 tables 的错误方法,也许他们会告诉您迷路。无论哪种方式,这都是最好的方法:您已将顾虑提高到适当的水平。

祝你好运。

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

这样的语句

这是Oracle支持文档中提到的"official"解决方案 如何在临时删除期间诊断 ORA-14452 TABLE(文档 ID 800506.1)。我过去曾成功地使用过这种方法,原因略有不同。 终止会话需要更高的权限并且可能很棘手;可能需要杀死,等待,然后再试几次。

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

这里值得考虑的另一种方法是重新考虑是否需要临时 table。

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

失败通常因不了解 PL/SQL-based 逐行处理几乎在每个方面都不如基于 SQL 的集合处理而更加复杂——更慢、代码更复杂、更冗长,而且更容易出错——但是 Oracle 为 SQL 处理提供了很多其他强大的特性,即使需要它,它通常也可以直接集成到 SQL SELECT 语句中。

附带说明一下,在为报告和 ETL 编写 Oracle 代码的 20 年中,我只需要使用一次逐行处理,从来不需要使用临时 table。

您可以通过 运行 查看所有 运行 会话:

SELECT * FROM V$SESSION

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

ALTER SYSTEM DISCONNECT SESSION ‘sid,serial#’ POST_TRANSACTION

而下面的命令就像kill -9;它擦除 O/S 进程:

ALTER SYSTEM DISCONNECT SESSION ‘sid,serial#’ IMMEDIATE

后者在终止阻止您删除临时文件的会话方面效率最高 table。但是,请谨慎使用,因为它是一个非常强力的函数。会话终止后,您可以删除临时文件 table 而不会出现错误。

您可以在此处详细了解终止会话的不同方法(我与该网站无关,当我遇到与您类似的问题时,我自己遇到了它): https://chandlerdba.wordpress.com/2013/07/25/killing-a-session-dead/