【问题标题】:Force Oracle Drop Global Temp Table强制 Oracle 删除全局临时表
【发布时间】:2015-12-02 02:35:33
【问题描述】:

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

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;

但是当我想删除这个表时问题就来了。 Oracle 不会让我删除表,它说:

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

【问题讨论】:

  • 全局临时表不应该以这种方式使用。它们应该被定义为模式的一部分,并且它们的结构不应随着每次使用/程序执行而改变。如果您有多个使用临时存储的程序,请为您需要的每个数据结构定义全局临时表。您还可以在查询中使用子查询分解,它可以在免费中创建临时表或使用 /*+ materialize */ 提示强制它(有一些限制)。
  • 我的老板坚持让我使用这种方式,临时表等,所以我别无选择。这是我在寻找使用临时表和使用其他部分中的数据的任何其他方式的第二天,您能指导我使用其他方式吗?
  • 那你老板不懂Oracle是怎么工作的。
  • 寻找另一份工作。你不会在那里学到任何有价值的东西。
  • 也许你是对的,但现在,在这个时刻我必须找到解决这个问题的方法,他坚持认为有办法做到这一点,当我解释更多细节时是没有办法通过临时表做到这一点的,我让他更生气,他在想我现在对 oracle 一无所知!我在第一个答案的 cmets 中解释了更多细节,如果您对如何强制 oracle 删除一些临时表而不考虑其他会话数据有任何想法,如果您与我分享,我将不胜感激。

标签: oracle plsql ddl temp-tables


【解决方案1】:

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

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/

【讨论】:

    【解决方案2】:

    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> 
    

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

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


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

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

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

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


    “我们的老板或其他人坚持按照他们的方式做某事,所以你无法改变”

    你遇到的是老板问题而不是编程问题。因此,就 StackOverflow 而言,它是题外话。但无论如何,这里有一些建议。

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

    1. 忽略ORA-14452 错误,继续生产,然后在一切都出现严重错误时使用“但你告诉我要这样做”的防御。这是最弱的玩法。
    2. 隐蔽地垃圾全局表并实现在多用户场景中工作的东西。这是高风险的,因为如果您搞砸了实施,您将没有任何防御措施。
    3. 与您的老板交谈。告诉他们您遇到了ORA-14452 错误,说您已经进行了一些调查,并且以这种方式使用全局临时表似乎是一个根本问题,但显然您忽略了一些事情。然后,询问他们之前实施过这个问题时是如何解决的。这可以有多种方式,也许他们有解决方法,也许他们会意识到这是使用全局临时表的错误方法,也许他们会告诉你迷路。无论哪种方式,这是最好的方法:您已将疑虑提出到适当的级别。

    祝你好运。

    【讨论】:

    • 不,还有一个问题,有一个我没有在代码中调用的过程,该过程将更改数据,并将其插入另一个全局临时表'd2v_temp'。有两张表,一张是v2d_temp,一张是d2v_temp,一张是我的数据,另一张是程序转换后的数据。程序将从 d2v_temp 中读取数据并将其转换并将新数据放入 v2d_temp。
    • 您提供的详细信息越少,这似乎是一个需要全局临时表的问题。老板告诉您使用它们这一事实意味着这是一个政治问题,而不是技术问题,因此超出了我们的职权范围。
    • 我知道这是一个政治问题,但我的问题是要找到问题的答案:有没有办法删除依赖于 oracle 中的会话的全局临时表?如果我们有这样的方法,我的问题就解决了。
    • 当然你也遇到过像我这样的情况,你的老板或者其他人坚持按照他们的方式做事,所以你无法改变,你可以通过一些特殊的方式或者一些技巧让你可以用肮脏的方式做事!
    【解决方案3】:

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

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

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

    附带说明一下,在编写用于报告和 ETL 的 Oracle 代码的 20 年中,我只需要使用一次逐行处理,而无需使用临时表。

    【讨论】:

      【解决方案4】:

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

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

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

      【讨论】:

      • 如果我关闭会话会影响其他用户,还是会给运行在 tomcat 6.0 上的整个应用程序带来一些问题?
      • 哦,是的,杀死会话非常危险,可能会导致各种问题。应用程序终止会话极为罕见。这是完成这项工作的唯一方法,但我并不是真的建议你这样做。相反,您可能想向您的 DBA 提及它;他们可能会坚持让你找到另一种方法。
      • 还好这个任务交给我了,他分配给了另一个人,他发现这也不可能发生,所以我们再讨论一下,改变我们要执行这个任务的方式
      猜你喜欢
      • 2011-12-17
      • 2017-10-02
      • 2023-03-27
      • 1970-01-01
      • 2010-12-07
      • 2016-04-02
      • 1970-01-01
      • 1970-01-01
      • 2012-02-13
      相关资源
      最近更新 更多