强制 Oracle 删除全局临时表

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/32423397/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-09 00:56:19  来源:igfitidea点击:

Force Oracle Drop Global Temp Table

oracleplsqlddltemp-tables

提问by Naeem Baghi

In our project I create some global temp table that will be like these:

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

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;

but the problem comes when I want to drop this table. Oracle will not let me to drop the table, and it says:

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

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

I have to use this table in some procedure but it may be changed dependent to other reports. So I should always drop the table then I should recreate it with my needed fields.

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

I have to use this for some business reasons so it is not possible for me to use tables, or other things. I can use just temp tables. I tried on commit delete rows, but when I call my procedure to use the data in this table there are no more rows in the table and they have been deleted.

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

Any helps will greatly appreciated, thanks in advance

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

/// EDIT

/// 编辑

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

回答by APC

Oracle global temporary tables are not transient objects. They are proper heap tables. We create them once and any sessioncan use them to store data which is visible only to that session.

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

The temporary aspect is that the data is not persistent beyond one transaction or one session. The key implementation detail is that the data is written to a temporary tablespace not a permanent one. However, the data is still written to - and read from - disk, so there is a notable overhead to the use of global temporary tables.

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

The point is we are not supposed to drop and recreate temporary tables. If you're trying to port SQL Server style logic into Oracle then you should consider using PL/SQL collections to maintain temporary data in-memory. Find out more.

关键是我们不应该删除和重新创建临时表。如果您尝试将 SQL Server 风格的逻辑移植到 Oracle 中,那么您应该考虑使用 PL/SQL 集合来维护内存中的临时数据。 了解更多。

The specific cause of ORA-14452is that we cannot drop a global temporary table which has session scope persistence if it has contained data during the session. Even if the table is currently empty...

的具体原因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>

The solution is to end the session and re-connect, or (somewhat bizarrely) to truncate the table and then drop it.

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

SQL> truncate table gtt23;

Table truncated.

SQL> drop table gtt23;

Table dropped.

SQL> 

If some other session is using the global temporary table - and that is possible (hence the globalnomenclature) then you won't be able to drop the table until all the sessions disconnect.

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

So the real solution is to learn to use global temporary tables properly: create specific global temporary tables to match each report. Or, as I say, use PL/SQL collections instead. Or, even, just learn to write well-tuned SQL. Often we use temporary tables as a workaround to a poorly-written query which could be saved with a better access path.

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



Having looked at your full code, the flow seems even more bizarre:

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

  1. Drop and re-create a global temporary table
  2. Populate temporary table
  3. Select from temporary table into PL/SQL array
  4. Insert into actual table using bulk insert from PL/SQL array
  1. 删除并重新创建全局临时表
  2. 填充临时表
  3. 从临时表中选择到 PL/SQL 数组
  4. 使用从 PL/SQL 数组批量插入插入实际表

There's so much overhead and wasted activity in here. All you need to do is take the data you insert into v2d_tempand directly populate vertical_design, ideally with an INSERT INTO ... SELECT * FROM statement. You will require some pre-processing to convert a JSON array into a query but that is easy to achieve in either Java or PL/SQL.

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

It seems certain to me that global temporary tables are not the right solution for your scenario.

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



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

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

What you have is a Boss Problemnot a Programming Problem. Consequently it is off-topic as far as StackOverflow goes. But here are some suggestions anyway.

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

The key thing to remember is that we are not talking about a compromise on some sub-optimal architecture: what your boss proposes clearly won't workin a multi-user environment. so, your options are:

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

  1. Ignore the ORA-14452error, proceed into production and then use the "but you told me to" defence when it all goes horribly wrong. This is the weakest play.
  2. Covertly junk the global tables and implement something which will work in a multi-user scenario. This is high-risk because you have no defence if you botch the implementation.
  3. Speak to your boss. Tell them you're running into the ORA-14452error, say you have done some investigation and it appears to a fundamental issue with using global temporary tables in this fashion but obviously you've overlooked something. Then, ask them how they got around this problem when they've implemented it before. This can go several ways, maybe they have a workaround, maybe they'll realise that this is the wrong way to use global temporary tables, maybe they'll tell you to get lost. Either way, this is the best approach: you've raised concerns to the appropriate level.
  1. 忽略ORA-14452错误,继续生产,然后当一切都出现可怕的错误时使用“但你告诉我要”的防御。这是最弱的玩法。
  2. 秘密地丢弃全局表并实现一些可以在多用户场景中工作的东西。这是高风险的,因为如果你搞砸了实施,你就没有防御能力。
  3. 和你的老板谈谈。告诉他们您遇到了ORA-14452错误,假设您已经进行了一些调查,并且以这种方式使用全局临时表似乎是一个基本问题,但显然您忽略了一些东西。然后,询问他们以前实施过这个问题时是如何解决这个问题的。这可以有多种方式,也许他们有解决方法,也许他们会意识到这是使用全局临时表的错误方法,也许他们会告诉你迷路了。无论哪种方式,这是最好的方法:您已将问题提出到适当的级别。

Good luck.

祝你好运。

回答by David Aldridge

Another approach worth considering here is to rethink whether you need a temporary table at all.

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

It is a very common programming practice among those who transition from other RDBMSs to Oracle to overuse them, because they do not understand that you can use such features as Common Table Expressions to implicitly materialise a temporary result set that can be referenced in other parts of the same query, and on other systems it has become natural to write data to a table and then select from it.

对于那些从其他 RDBMS 过渡到 Oracle 过度使用它们的人来说,这是一种非常常见的编程实践,因为他们不明白您可以使用 Common Table Expressions 等特性隐式物化一个临时结果集,该结果集可以在其他部分引用同样的查询,在其他系统上,将数据写入表然后从中选择已经变得很自然。

The failure is usually compounded by not understanding that PL/SQL-based row by row processing is inferior in almost every respect to SQL-based set processing -- slower, more complex to code, more wordy, and more error prone -- but Oracle presents so many other powerful features for SQL processing that even when it is required it can generally be integrated directly into a SQL SELECT statement anyway.

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

As a side-note, in 20 years of writing Oracle code for reporting and ETL, I only needed to do use row-by-row processing once, and never needed to use a temporary table.

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

回答by Jon Heller

Killing sessions is the only way to work around ORA-14452 errors. Use the data dictionary to find other sessions using the temporary table and kill them with a statement like alter system kill session 'sid,seriall#,instance_id';.

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

This is the "official" solution mentioned in the Oracle support document HOW TO DIAGNOSE AN ORA-14452 DURING DROP OF TEMPORARY TABLE (Doc ID 800506.1). I've successfully used this method in the past, for a slightly different reason. Killing sessions requires elevated privileges and can be tricky; it may require killing, waiting, and trying again several times.

这是 Oracle 支持文档如何在 DROP OF TEMPORARY TABLE (Doc ID 800506.1) 期间诊断 ORA-14452 中提到的“官方”解决方案 。我过去曾成功地使用过这种方法,原因略有不同。终止会话需要更高的权限并且可能很棘手;它可能需要多次杀死、等待和重试。

This solution is almost certainly a bad idea for many reasons. Before you implement this, you should try to leverage this information as proof that this is the wrong way to do it. For example, "Oracle documentation says this method requires alter systemprivilege, which is dangerous and raises some security issues...".

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

回答by dearsina

You can see all running sessions by running:

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

SELECT * FROM V$SESSION

To kill a session, you have a few options. The following command waits for the current in-flight transactions to complete before disconnecting:

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

ALTER SYSTEM DISCONNECT SESSION ‘sid,serial#' POST_TRANSACTION

While the following command is just like kill -9; it wipes the O/S process out:

虽然下面的命令就像 kill -9; 它消除了 O/S 过程:

ALTER SYSTEM DISCONNECT SESSION ‘sid,serial#' IMMEDIATE

The latter is the most efficient in killing the session that is preventing you from removing a temp table. However, use with caution, as it's a pretty brute force function. Once the session is terminated, you can remove the temp table without getting errors.

后者是杀死阻止您删除临时表的会话的最有效方法。但是,请谨慎使用,因为它是一个非常强力的函数。会话终止后,您可以删除临时表而不会出错。

You can read more about the different ways of killing a session here (I'm not affiliated with this website, I came across it myself when I had a similar problem to yours): https://chandlerdba.wordpress.com/2013/07/25/killing-a-session-dead/

您可以在此处阅读有关终止会话的不同方法的更多信息(我不隶属于该网站,当我遇到与您类似的问题时,我自己遇到了它):https: //chandlerdba.wordpress.com/2013/ 07/25/killing-a-session-dead/