web-dev-qa-db-ja.com

Oracle Drop Global Temp 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;

しかし、このテーブルを削除したいときに問題が発生します。オラクルは私にテーブルを削除させません、そしてそれは言います:

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

Oracleグローバル一時テーブルは一時オブジェクトではありません。それらは適切なヒープテーブルです。それらを一度作成すると、any sessionはそれらを使用して、that sessionにのみ表示されるデータを格納できます。

一時的な側面は、データが1つのトランザクションまたは1つのセッションを超えて永続的でないことです。重要な実装の詳細は、データが永続的なテーブルスペースではなく一時的なテーブルスペースに書き込まれることです。ただし、データは引き続きディスクに書き込まれ、ディスクから読み取られるため、グローバル一時テーブルの使用には顕著なオーバーヘッドがあります。

ポイントは、一時テーブルを削除して再作成することを想定していないことです。 SQL ServerスタイルのロジックをOracleに移植しようとしている場合は、PL/SQLコレクションを使用して一時データをメモリ内に維持することを検討する必要があります。 詳しくはこちら

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エラーが発生していることをお客様に伝えます。調査を行ったところ、この方法でグローバル一時テーブルを使用することの根本的な問題のように見えますが、明らかに見落としていることを伝えます。次に、この問題を以前に実装したことがある場合に、この問題をどのように回避したかを尋ねます。これにはいくつかの方法があります。回避策がある場合もあれば、これがグローバル一時テーブルを使用する間違った方法であることに気づく場合もあれば、迷子になるように指示する場合もあります。いずれにせよ、これが最善のアプローチです。あなたは懸念を適切なレベルに引き上げました。

幸運を。

20
APC

ここで検討する価値のある別のアプローチは、一時テーブルが必要かどうかを再考することです。

他のRDBMSからOracleに移行して使い過ぎる人の間では、非常に一般的なプログラミング手法です。共通テーブル式などの機能を使用して、他の部分で参照できる一時的な結果セットを暗黙的に具体化できることを理解していないためです。同じクエリ、および他のシステムでは、データをテーブルに書き込んでからそれを選択するのが自然になりました。

PL/SQLベースの行ごとの処理は、SQLベースのセット処理のほとんどすべての点で劣っていることを理解しないことで、通常、障害がさらに悪化します-遅く、コードが複雑になり、より複雑で、エラーが発生しやすくなります-しかし、Oracle SQL処理には他にも非常に多くの強力な機能があり、必要な場合でも、SQL SELECTステートメントに直接統合できます。

補足として、レポート作成とETLのためのOracleコードを20年間書いている間に、行ごとの処理を一度だけ使用する必要があり、一時テーブルを使用する必要はありませんでした。

4
David Aldridge

セッションの強制終了は、ORA-14452エラーを回避する唯一の方法です。データディクショナリを使用して、一時テーブルを使用する他のセッションを見つけ、alter system kill session 'sid,seriall#,instance_id';のようなステートメントでそれらを強制終了します。

これは、Oracleサポートドキュメントで言及されている「公式の」ソリューションです一時テーブルのドロップ中にORA-14452を診断する方法(Doc ID 800506.1)。私は過去にこの方法を成功させてきましたが、少し異なる理由があります。セッションを強制終了するには昇格された特権が必要であり、注意が必要です。強制終了、待機、および数回の再試行が必要になる場合があります。

このソリューションは、多くの理由でほぼ間違いなく悪い考えです。これを実装する前に、これが間違った方法であることを証明するために、この情報を活用する必要があります。たとえば、「Oracleのドキュメントでは、このメソッドにはalter system権限が必要であると記載されています。これは危険であり、いくつかのセキュリティ問題を引き起こします...」です。

3
Jon Heller

次のコマンドを実行すると、実行中のすべてのセッションを確認できます。

SELECT * FROM V$SESSION

セッションを終了するには、いくつかのオプションがあります。次のコマンドは、現在の処理中のトランザクションが完了するのを待ってから切断します。

ALTER SYSTEM DISCONNECT SESSION ‘sid,serial#’ POST_TRANSACTION

次のコマンドはkill -9に似ていますが、 O/Sプロセスを一掃します。

ALTER SYSTEM DISCONNECT SESSION ‘sid,serial#’ IMMEDIATE

後者は、セッションを強制終了するのに最も効率的で、一時テーブルを削除できません。ただし、これはかなりブルートフォースの関数であるため、注意して使用してください。セッションが終了したら、エラーが発生することなく一時テーブルを削除できます。

セッションを強制終了するさまざまな方法の詳細については、こちらをご覧ください(私はこのWebサイトとは関係がありません。同様の問題があったときに自分で見つけました): https://chandlerdba.wordpress。 com/2013/07/25/killing-a-session-dead /

0
dearsina