ORA-39405 Error TSTZ al hacer expdp/impdp. Otra forma de importar los datos

Estoy completamente seguro que el 90% de los DBAs existentes en el planeta tierra se han encontrado con este error alguna vez.
Al igual que yo, te has puesto a buscar alternativas, soluciones, parches, workarounds, algún truco que alguien haya publicado por la red, a ver si consigues llevar a cabo la tarea que te han encomendado.

En los últimos días he leído mucho sobre este tema. He leído artículos que cuentan desde las soluciones más aconsejables y completas hasta las más descabelladas. Por ejemplo actualizar o parchear la base de datos donde no puedes importar los datos o copiar la librería de timezone de una base de datos a otra, y rezarle a todo lo que sepas para que la importación vaya bien y no crear una corrupción de datos o provocar un estallido de la base de datos. ¿Crees que sabes cual es la aconsejable y cual la descabellada?

En mi caso concreto, no podía hacer trucos de magia y jugármela. Tampoco podía ni me permitían actualizar o parchear con un nuevo Release Update la base de datos destino para tener el mismo nivel de timezone y poder realizar la importación correctamente.

Al final he podido importar los datos, usando una utilidad de toda la vida, pero en la que quizá nunca hubiera caído en usar de no ser por un amigo y compañero de profesión que me dio la clave.

¿Quieres que te cuente el secreto? Acompáñame en esta historia.

Contexto

Te pongo en situación para entender de donde partimos.

Tenemos dos bases de datos, en distintos servidores.

Origen: Servidor Solaris / Base de Datos Oracle 19.23
Destino: Servidor Linux / Base de Datos Oracle 19.8

El Error

[oracle@servidor1 ]$ impdp "/ as sysdba" directory=IMP logfile=import_timezone.log schemas=DEMO dumpfile=import_timezone.dmp

Import: Release 19.0.0.0.0 - Production on Tue Mar 11 14:33:39 2025
Version 19.8.0.0.0

Connected to: Oracle Database 19c Enterprise Edition Release 19.0.0.0.0 - Production
ORA-39002: operacion no valida
ORA-39405: El pump de datos de Oracle no soporta la importacion de una base de datos de origen con la version 43 de TSTZ a una base de datos de destino con la version 32 de TSTZ.

¿Por qué ocurre esto? ¿Por qué nos aparece el error de timezone?

Este error ocurre cuando la versión de timezone del destino es inferior a la versión de timezone del origen. Podemos consultar esta información con la siguiente consulta:

SELECT * FROM V$TIMEZONE_FILE;

También, puedes consultar si hay disponible una versión superior a la que puedas actualizar.

SELECT DBMS_DST.GET_LATEST_TIMEZONE_VERSION FROM DUAL;

Para entenderlo un poco mejor, vamos a verlo con un ejemplo.

  • Origen:
SQL> SELECT * FROM V$TIMEZONE_FILE;

FILENAME                VERSION     CON_ID
-------------------- ---------- ----------
timezlrg_43.dat              43          0
  • Destino:
SQL> SELECT * FROM V$TIMEZONE_FILE;

FILENAME		VERSION     CON_ID
-------------------- ---------- ----------
timezlrg_32.dat 	     32 	 0

Esta librería se puede localizar en la siguiente ruta. (No quiero incitarte a que cambies el fichero por otro que te venga mejor).

$ORACLE_HOME/oracore/zoneinfo/timezlrg_43.dat

Como puedes ver, en el origen tenemos la versión 43, y en el destino una versión inferior, la versión 32. El mensaje de error nos lo indica igualmente muy claro.

Posibles soluciones

1. Actualizar la base de datos o aplicar un nuevo RU.

En mi opinión, la mejor solución y la más fiable es la de aplicar un Release Update a la base de datos, que actualice la versión de timezone y poder realizar cualquier acción con seguridad.

En caso de que tras aplicar el parche y subir la versión de la base de datos no se actualice el timezone, te dejo aquí la forma de actualizar solo este componente, de la mano de Tim Hall.

Upgrade the Database Time Zone File Using the DBMS_DST Package

2. Sustituir los ficheros de timezone desde el sistema operativo (NO RECOMENDADO)

Como ya dejé caer anteriormente, hay quien ha falseado los ficheros de timezone por una versión más reciente, cosa que él mismo no recomienda y no aconseja hacerlo, pero en alguna ocasión, le ha salvado del apuro.

Este «truco» lo he podido ver en el blog de Ora.Ninja

ORA-39405: El pump de datos de Oracle no soporta la importación de una base de datos de origen con la versión XX de TSTZ a una base de datos de destino con la versión XX de TSTZ

Mi Solución

Comentando esta situación con un compañero de profesión con el que a veces hacemos coworking matutino, vespertino o cuando se presenta, me sugirió probar una utilidad que aunque está en desuso, ahí sigue estando, y en esta ocasión, nos ha aportado solución a nuestro problema.

Te hablo de la utilidad exp. Si, si, exp, nada de expdp. El antiguo exp, a secas, sin datapump.

Esta utilidad tiene de bueno que no tiene en cuenta el timezone, pero también tiene sus limitaciones e inconvenientes. Por ejemplo, el principal inconveniente que yo he encontrado es que no admite algunos tipos de datos, que cada vez son más comunes.

Algunos tipos de datos que exp NO puede exportar correctamente
  • CLOB y BLOB grandes (superiores a 64 KB): exp tiene problemas con lobs grandes y no los maneja de manera eficiente.
  • BFILE: Los archivos físicos del SO referenciados por BFILE no se exportan porque exp solo almacena la ruta del archivo, no su contenido.
  • XMLType: Solo exporta este tipo de dato cuando es almacenado como CLOB.
  • TDE: exp no exporta correctamente los datos encriptados con Transparent Data Encryption.

En la tarea que yo tenía que realizar, tenía que mover un esquema pequeñito. El esquema tenía pocos objetos, y además, eran objetos creados con la aplicación ODI en una versión también antigua, por lo que es posible que nos sirviera.

Comprobé los tipos de segmentos que tenía en origen y no me lo pensé.

SQL> select owner, segment_type from dba_segments where owner='ESQUEMA_DEMO' group by owner, segment_type  

OWNER            SEGMENT_TYPE
---------------- ------------------
ESQUEMA_DEMO     INDEX
ESQUEMA_DEMO     LOBSEGMENT
ESQUEMA_DEMO     TABLE
ESQUEMA_DEMO     LOBINDEX

Ejecuté el export en origen de la siguiente manera:

servidor1:/oracle/temp $ export NLS_LANG=.WE8ISO8859P15
servidor1:/oracle/temp $ exp " '/ as sysdba' " owner=ESQUEMA_DEMO FILE=exp_demo.dmp LOG=log_exp_demo.log

Importante indicarle el NLS_LANG que usa tu base de datos, para que no coja otro distinto por defecto, y empiecen a aparecer errores de caracteres y también de estadísticas de este tipo:

EXP-00091: Exporting questionable statistics.

Una vez tenemos el export terminado, lo pasamos a destino y lo importamos:

[oracle@servidor2 ]$ export NLS_LANG=.WE8ISO8859P15
[oracle@servidor2 ]$ imp " '/ as sysdba' " FROMUSER=ESQUEMA_DEMO TOUSER=ESQUEMA_DEMO19 FILE=exp_demo.dmp LOG=log_imp_demo19.log STATISTICS=none

En el ejemplo te pongo como se haría con un remap schema, si quieres importarlo con otro nombre. Lo que no se puede hacer es un remap de tablespace, por lo que será necesario que los tablespces los crees antes de ejecutar el imp.

Al finalizar la importación, comprobé el tipo y cantidad de objetos que había dentro del esquema, por si había alguna diferencia.

-- ORIGEN
SQL>    col owner for a25
        col object_type for a20
        select owner, object_type, count(1) total
        from dba_objects
        where owner = '&owner'
        group by owner, object_type
        order by owner, object_type; 
Enter value for owner: ESQUEMA_DEMO
old   3:        where owner = '&owner'
new   3:        where owner = 'ESQUEMA_DEMO'
			  
OWNER                OBJECT_TYPE               TOTAL
------------- ------ -------------------- ----------
ESQUEMA_DEMO         INDEX                       922
ESQUEMA_DEMO         LOB                         124
ESQUEMA_DEMO         PROCEDURE                     1
ESQUEMA_DEMO         TABLE                       187
ESQUEMA_DEMO         VIEW                         75

-- DESTINO
SQL> col owner for a25
col object_type for a20
select owner, object_type, count(1) total
from dba_objects
where owner = '&owner'
group by owner, object_type
order by owner, object_type;
Introduzca un valor para owner: ESQUEMA_DEMO19
antiguo   3: where owner = '&owner'
nuevo	3: where owner = 'ESQUEMA_DEMO19'

OWNER			  OBJECT_TYPE		    TOTAL
------------------------- -------------------- ----------
ESQUEMA_DEMO19            INDEX 		      922
ESQUEMA_DEMO19            LOB			      124
ESQUEMA_DEMO19            PROCEDURE			1
ESQUEMA_DEMO19            TABLE 		      187
ESQUEMA_DEMO19            VIEW			       75

Finalmente, todo se había importado bien, y la aplicación ODI conectó correctamente con este esquema. Las pruebas que se realizaron fueron satisfactorias y con esta utilidad, hemos conseguido salvar este problema que nos impedía tener todos los esquemas de esta aplicación en la misma base de datos.

Si alguna vez tienes problemas de timezone con datapump, recuerda que exp es tu amigo y te puede ayudar en algunos casos. 🙌🏼

Agregar discos a un diskgroup en ASM. SQL*Plus VS asmca

Deja un comentario