web-dev-qa-db-fra.com

Forcer Oracle Drop Global Temp Table

Dans notre projet, je crée une table temporaire globale qui ressemblera à ceci:

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;

mais le problème survient lorsque je veux supprimer ce tableau. Oracle ne me laissera pas supprimer le tableau, qui se lit comme suit:

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

Je dois utiliser ce tableau dans certaines procédures, mais il peut être modifié en fonction d'autres rapports. Je devrais donc toujours supprimer la table, puis la recréer avec les champs nécessaires.

Je dois utiliser cela pour des raisons commerciales, il ne m'est donc pas possible d'utiliser des tableaux ou autre chose. Je ne peux utiliser que des tables temporaires. J'ai essayé les lignes de suppression de commit, mais lorsque j'appelle ma procédure pour utiliser les données de cette table, il n'y a plus de lignes dans la table et elles ont été supprimées.

Toute aide sera grandement appréciée, Merci d'avance

/// MODIFIER

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

Les tables temporaires globales Oracle ne sont pas des objets transitoires. Ce sont de bonnes tables de tas. Nous les créons une fois et toute session peut les utiliser pour stocker des données visibles uniquement par cette session.

L'aspect temporaire est que les données ne sont pas persistantes au-delà d'une transaction ou d'une session. Le détail de l'implémentation réside dans le fait que les données sont écrites dans un tablespace temporaire et non permanent. Toutefois, les données sont toujours écrites sur le disque et lues à partir de ce dernier. Il existe donc une surcharge considérable pour l'utilisation de tables temporaires globales.

Le fait est que nous ne sommes pas censés supprimer et recréer des tables temporaires. Si vous essayez de porter une logique de type SQL Server dans Oracle, envisagez d'utiliser des collections PL/SQL pour conserver des données temporaires en mémoire. En savoir plus.

La cause spécifique de ORA-14452 est que nous ne pouvons pas supprimer une table temporaire globale ayant une persistance d'étendue de session si elle contient des données pendant la session. Même si la table est actuellement vide ...

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>

La solution consiste à mettre fin à la session et à vous reconnecter, ou (quelque peu bizarrement) à tronquer la table, puis à la supprimer.

SQL> truncate table gtt23;

Table truncated.

SQL> drop table gtt23;

Table dropped.

SQL> 

Si une autre session utilise la table temporaire globale, ce qui est possible (d'où la nomenclature globale), vous ne pourrez pas supprimer la table tant que toutes les sessions ne se seront pas déconnectées.

La vraie solution consiste donc à apprendre à utiliser correctement les tables temporaires globales: créez des tables temporaires globales spécifiques correspondant à chaque rapport. Ou, comme je le dis, utilisez plutôt des collections PL/SQL. Ou même, apprenez simplement à écrire du code SQL bien ajusté. Nous utilisons souvent des tables temporaires comme solution de contournement à une requête mal écrite qui pourrait être enregistrée avec un meilleur chemin d'accès.


Après avoir examiné votre code complet, le flux semble encore plus bizarre:

  1. Supprimer et recréer une table temporaire globale
  2. Remplir une table temporaire
  3. Sélectionner une table temporaire dans un tableau PL/SQL
  4. Insérer dans la table réelle en utilisant une insertion en bloc à partir d'un tableau PL/SQL

Il y a tellement de frais généraux et de gaspillage d'activité ici. Tout ce que vous avez à faire est de prendre les données que vous insérez dans v2d_temp et de renseigner directement vertical_design, idéalement avec une instruction INSERT INTO ... SELECT * FROM. Un pré-traitement est nécessaire pour convertir un tableau JSON en une requête, mais cela est facile à réaliser en Java ou en PL/SQL.

Il me semble certain que les tables temporaires globales ne sont pas la bonne solution pour votre scénario.


"notre patron ou d'autres personnes persistent à faire quelque chose par leur chemin, de sorte que vous ne pouvez pas changer cela"

Ce que vous avez est un problème Boss et non pas un problème de programmation. Par conséquent, il est hors sujet pour StackOverflow. Mais voici quelques suggestions quand même.

L'important, c'est que nous ne parlions pas d'un compromis sur une architecture sous-optimale: ce que votre patron propose clairement ne fonctionnera pas dans un environnement multi-utilisateur. alors, vos options sont:

  1. Ignorez l'erreur ORA-14452, passez en production puis utilisez le "mais vous m'avez dit de" défendre quand tout va terriblement mal. C'est le jeu le plus faible.
  2. Caché secrètement les tables globales et implémenter quelque chose qui fonctionnera dans un scénario multi-utilisateur. Cela présente un risque élevé, car vous n'avez aucune défense si vous bloquez la mise en œuvre.
  3. Parlez à votre patron. Dites-leur que vous rencontrez l'erreur ORA-14452, dites que vous avez effectué des recherches et que l'utilisation de tables temporaires globales semble poser un problème fondamental, mais vous avez évidemment oublié quelque chose. Puis, demandez-leur comment ils ont résolu ce problème quand ils l’ont déjà mis en œuvre. Cela peut aller de plusieurs manières, peut-être qu’elles ont une solution de contournement, qu’elles réaliseront peut-être que c’est la mauvaise façon d’utiliser des tables temporaires globales, peut-être qu’elles vous diront de vous perdre. Quoi qu’il en soit, c’est la meilleure approche: vous avez soulevé des préoccupations au niveau approprié.

Bonne chance.

16
APC

Une autre approche à considérer ici consiste à repenser si vous avez besoin d'une table temporaire.

C’est une pratique de programmation très courante chez ceux qui passent d’autres SGBDR à Oracle, car ils ne comprennent pas que vous pouvez utiliser des fonctionnalités telles que Common Table Expressions pour matérialiser implicitement un ensemble de résultats temporaire pouvant être référencé dans d’autres parties de. même requête, et sur d’autres systèmes, il est devenu naturel d’écrire des données dans une table, puis de sélectionner celle-ci.

L'échec est généralement aggravé par le fait de ne pas comprendre que le traitement ligne par ligne basé sur PL/SQL est inférieur à presque tous les égards au traitement des ensembles basé sur SQL - plus lent, plus complexe à coder, plus textuel et plus sujet aux erreurs - mais Oracle présente tellement d'autres fonctionnalités puissantes pour le traitement SQL que même quand cela est nécessaire, il peut généralement être intégré directement dans une instruction SQL SELECT.

En 20 ans de rédaction de code Oracle pour la génération de rapports et d'ETL, je n'avais besoin d'utiliser qu'une seule fois le traitement ligne par ligne et je n'avais jamais eu besoin d'utiliser une table temporaire.

4
David Aldridge

Tuer des sessions est le seul moyen de contourner les erreurs ORA-14452. Utilisez le dictionnaire de données pour rechercher d'autres sessions à l'aide de la table temporaire et supprimez-les Avec une instruction telle que alter system kill session 'sid,seriall#,instance_id';.

C’est la solution "officielle" mentionnée dans le document de support Oracle POUR DIAGNOSTIQUER UNE ORA-14452 LORS DE L’AJUSTEMENT DE LA TABLE TEMPORAIRE (Doc ID 800506.1) . J'ai utilisé cette méthode avec succès par le passé, pour une raison légèrement différente. Tuer des sessions nécessite des privilèges élevés et peut s'avérer délicat. il faudra peut-être tuer, attendre et réessayer plusieurs fois.

Cette solution est presque certainement une mauvaise idée pour plusieurs raisons. Avant de mettre cela en œuvre, vous devriez essayer de tirer parti de ces informations pour prouver que c'est La mauvaise façon de le faire. Par exemple, "La documentation Oracle indique que cette méthode nécessite le privilège alter system, ce qui est dangereux et soulève quelques Problèmes de sécurité ...".

2
Jon Heller

Vous pouvez voir toutes les sessions en cours d'exécution en exécutant:

SELECT * FROM V$SESSION

Pour tuer une session, vous avez quelques options. La commande suivante attend que les transactions en vol en cours se terminent avant de se déconnecter:

ALTER SYSTEM DISCONNECT SESSION ‘sid,serial#’ POST_TRANSACTION

Bien que la commande suivante ressemble à kill -9; il efface le processus O/S:

ALTER SYSTEM DISCONNECT SESSION ‘sid,serial#’ IMMEDIATE

Ce dernier est le plus efficace pour tuer la session qui vous empêche de supprimer une table temporaire. Cependant, utilisez-le avec prudence, car il s'agit d'une fonction assez brutale. Une fois la session terminée, vous pouvez supprimer la table temporaire sans générer d'erreurs.

Vous pouvez en savoir plus sur les différentes façons de tuer une session ici (je ne suis pas affilié à ce site, je l’ai découvert moi-même quand j’avais un problème similaire au vôtre): https: // chandlerdba .wordpress.com/2013/07/25/kill-a-session-dead/

0
dearsina