La Guía Completa de Mapeo de Tipos de Datos de Oracle a PostgreSQL

En resumen La mayoría de los mapeos de tipos de datos de Oracle a PostgreSQL son sencillos.
Los peligrosos no lo son.
Oracle DATE incluye un componente de tiempo que PostgreSQL DATE descarta silenciosamente.
Oracle NUMBER se mapea a NUMERIC por defecto, lo cual es correcto pero lento para cargas de trabajo enteras.
Las cadenas vacías en Oracle son NULL; en PostgreSQL no lo son.
Esta guía te ofrece la tabla de correspondencia completa y cubre las cinco trampas que causan corrupción silenciosa de datos durante la migración.


El problema de datos más común en un ejercicio de mapeo de tipos de datos de Oracle a PostgreSQL no es una columna faltante o una importación fallida.

Es un mapeo que parece correcto, pasa la validación y cambia tus datos silenciosamente.

Proyectos de migración donde las fechas perdieron su componente de hora, la aritmética de enteros se ralentizó drásticamente y la lógica de la aplicación se rompió porque las cadenas vacías dejaron de comportarse como NULL, ninguno de estos problemas fue obvio en la etapa de conversión del esquema.

Todos eran prevenibles.

Esta guía te ofrece la tabla de mapeo completa y explica las decisiones que importan.

Si estás al inicio de tu Migración de Oracle a PostgreSQL, guarda esta página en marcadores.

Volverás a ello.



Tabla Completa de Mapeo de Tipos de Datos de Oracle a PostgreSQL

La siguiente tabla cubre todos los tipos comunes de Oracle.

La columna “Safe Default” es lo que ora2pg usa automáticamente.

La columna “Recomendado” es la que realmente deberías usar después de analizar tus datos.

Tipo de OraclePredeterminado seguroRecomendadoNotas
NÚMERONUMÉRICOENTERO, BIGINT o NUMÉRICODepende de la escala y el alcance; consulte la sección a continuación
Número(p, 0)NUMERIC(p)SMALLINT / INTEGER / BIGINTUtilice tipos enteros para un mejor rendimiento
NÚMERO(p, s)NUMERIC(p, s)NUMERIC(p, s)Coincidencia exacta
VARCHAR2(n)VARCHAR(n)VARCHAR(n) o TEXTn es caracteres en PG; puede requerir ajuste para columnas con semántica de bytes
NVARCHAR2(n)VARCHAR(n)VARCHAR(n)PG siempre es UTF-8; no se necesita un tipo unicode separado
CARACTER(n)CARACTER(n)CARACTER(n)Coincidencia exacta
FECHAMARCA DE TIEMPOMARCA DE TIEMPOOracle DATE incluye la hora, nunca mapear a PG DATE
MARCA DE TIEMPOMARCA DE TIEMPOMARCA DE TIEMPOCoincidencia exacta
MARCA DE TIEMPO CON ZONA HORARIATIMESTAMPTZTIMESTAMPTZLas semánticas de almacenamiento difieren; consulta la sección a continuación.
MARCA DE TIEMPO CON ZONA HORARIA LOCALTIMESTAMPTZTIMESTAMPTZMisma advertencia
INTERVALO AÑO A MESINTERVALOINTERVALOPG INTERVAL es un tipo flexible único
DÍA DEL INTERVALO A SEGUNDOINTERVALOINTERVALOProbar aritmética de fechas cruzando límites de mes/día
CLOBTEXTOTEXTOEl texto PG es ilimitado en la práctica (hasta ~1 GB a través de TOAST)
NCLOBTEXTOTEXTOMismo
BOLOBYTEABYTEA o lopara archivos grandes o casos de uso de transmisión
CRUDO(n)BYTEABYTEAReemplazo directo
LARGO CRUDOBYTEABYTEAObsoleto en Oracle; migrar cuanto antes
LARGOTEXTOTEXTOObsoleto en Oracle
XMLTYPEXMLXMLLa consulta PG XML utiliza XPATH() y XMLTABLE()
FLOTANTE BINARIOREALREALIEEE 754 de 4 bytes — coincidencia directa
BINARIO_DOBLEDOBLE PRECISIÓNDOBLE PRECISIÓNIEEE 754 de 8 bytes — correspondencia directa
BOOLEAN (Oracle 23c+)BOLEANOBOLEANOPre-23c: NUMBER(1) o CHAR(1) — se requiere conversión manual
ROWID / UROWIDNo hay equivalenteRediseño con clave primariaEl ctid de PG no es estable tras VACUUM o clustering
SDO_GEOMETRYNo incorporadoPOSTGIS GEOMETRYRequiere extensión PostGIS

¿Por qué Oracle DATE rompe todo en PostgreSQL?

Oracle DATE almacena fecha y hora. PostgreSQL DATE almacena solo fecha.
Mapear Oracle DATE a PostgreSQL DATE descarta silenciosamente el componente de hora de cada valor en esa columna — sin error, sin advertencia, los recuentos de filas siguen coincidiendo.
Siempre mapea Oracle DATE a PostgreSQL TIMESTAMP.

Esta es la corrupción silenciosa de datos más común en las migraciones de Oracle a PostgreSQL.

La importación se realiza correctamente.

Recuentos de filas coinciden.

Los datos son incorrectos.

El mapeo correcto es siempre Oracle DATE a PostgreSQL TIMESTAMP.

Según el Manual de Estrategias de Migración de AWS de Oracle a Aurora PostgreSQL, este es un mapeo obligatorio, no opcional.

ora2pg lo hace bien por defecto.

Dos problemas de seguimiento para verificar después de la reasignación:

TRUNC(date) en SQL y PL/SQL.
Los desarrolladores de Oracle utilizan frecuentemente TRUNC(SYSDATE) para eliminar el componente de tiempo y obtener la medianoche.
En PostgreSQL el equivalente es DATE_TRUNC('day', now()).
Cualquier consulta o procedimiento almacenado que utilice TRUNC de Oracle en una columna de fecha deberá actualizarse.

Comparaciones de fechas en la capa de aplicación.
Código que compara fechas con = TRUNC(SYSDATE) (comprobando los registros de hoy) se comporta de manera diferente una vez que la columna es TIMESTAMP.
Revisa toda la lógica de comparación de fechas en el código de la aplicación, no solo en la base de datos.


¿Cómo mapear Oracle NUMBER para un rendimiento óptimo?

Nunca aceptes NUMERIC para todas las columnas NUMBER.
NUMERIC utiliza aritmética de precisión arbitraria, lo cual es correcto pero lento para cargas de trabajo de enteros.
Analice el rango y la escala reales de cada columna: utilice INTEGER para valores de hasta 2.100 millones, BIGINT para el rango de 64 bits, NUMERIC(p,s) para precisión decimal fija.
Anular los valores predeterminados de ora2pg con la directiva MODIFY_TYPE.

El enfoque correcto es analizar tus datos reales primero:

  • Número(p, 0) donde los valores caben en 32 bits: use INTEGER (hasta 2.1 mil millones)
  • Número(p, 0) donde los valores caben en 64 bits: use BIGINT (hasta 9,2 × 10¹⁸)
  • NÚMERO(p, s) con precisión decimal fija: usar NUMERIC(p, s)
  • NÚMERO con ninguna precisión (cálculos flotantes): considere DOBLE PRECISIÓN para velocidad, NUMÉRICO para exactitud

AWS publicó un análisis en dos partes sobre cómo evaluar columnas Oracle NUMBER y asignar el tipo PostgreSQL correcto.

El proceso implica consultar Oracle para encontrar la distribución real de mínimo, máximo y escala para cada columna antes de decidir.

En ora2pg, anula el valor predeterminado con el MODIFICAR_TIPO directiva en tu archivo de configuración:

MODIFICAR_TIPO ÓRDENES.IMPORTE_TOTAL:numeric(15,2),ÓRDENES.CODIGO_ESTADO:integer

Esto es a nivel de columna.

Lo configuras por tabla después de analizar el perfil de datos real de cada columna.

Saltarse este paso y aceptar NUMERIC en todas partes es una de las causas más comunes de un mal rendimiento de PostgreSQL inmediatamente después de la migración.


¿Qué sucede con CLOB y BLOB de Oracle?

Oracle CLOB se mapea a PostgreSQL TEXT, que maneja valores de hasta aproximadamente 1 GB a través del almacenamiento TOAST; no se requieren cambios en la aplicación para los datos en sí.

Oracle BLOB se mapea a BYTEA para la mayoría de los casos de uso, o al tipo lo para streaming.

El trabajo de migración está reescribiendo las llamadas al paquete DBMS_LOB; no existe un equivalente en PostgreSQL.

La excepción es el código de aplicación que utiliza el paquete DBMS_LOB de Oracle: DBMS_LOB.READ, DBMS_LOB.WRITE, DBMS_LOB.GETLENGTH, etc.

Estas son APIs LOB específicas de Oracle.

Deben reescribirse para usar funciones de cadena SQL estándar en PostgreSQL.

No existe un equivalente de DBMS_LOB en PostgreSQL.

BLOB es más matizado.

PostgreSQL tiene dos opciones:

BYTEA almacena datos binarios en línea en la fila, hasta aproximadamente 1 GB.
Es más sencillo y funciona bien para imágenes, documentos y contenido binario de tamaño pequeño a mediano.

Objetos grandes (tipo lo) almacenar datos en el catálogo del sistema pg_largeobject y admitir acceso por streaming a través de lo_read y lo_write.
Esto está más cerca de la semántica BLOB de Oracle para aplicaciones que transmiten contenido binario en fragmentos.
Los objetos grandes requieren gestión explícita del ciclo de vida: debes llamar a lo_unlink() para eliminarlos, o usar la extensión lo para gestionar esto automáticamente mediante disparadores.

Para la mayoría de las migraciones, BYTEA es la opción correcta, a menos que tenga un requisito de transmisión específico.


Cinco Trampas de Tipo que Sorprenden a los Equipos

Estos no siempre aparecen en los informes automatizados de conversión de esquemas.

Rompen cosas en tiempo de ejecución, a menudo en casos extremos que no están cubiertos por pruebas básicas.

1. Cadena vacía es igual a NULL en Oracle.
Oracle VARCHAR2 trata una cadena vacía ('' ) como NULL.
PostgreSQL los trata como valores diferentes.
Cualquier lógica SQL o de aplicación que dependa de columna ES NULO para capturar tanto NULL como cadenas vacías fallará silenciosamente.
Cualquier restricción NOT NULL en una columna VARCHAR2 en Oracle no evita las cadenas vacías.
En PostgreSQL, sí previene las cadenas vacías.
Audita cada restricción NOT NULL y cada ES NULO / NO ES NULO revisa en tu código base antes del corte.

2. Semántica de bytes vs. caracteres de VARCHAR2.
VARCHAR2(20 BYTE) y VARCHAR2(20 CHAR) son diferentes en Oracle.
VARCHAR(n) en PostgreSQL siempre significa caracteres.
Una columna definida como VARCHAR2(20 BYTE) en una base de datos Oracle de un solo byte puede contener hasta 20 caracteres.
La misma columna migrada con codificación AL32UTF8 podría contener menos caracteres si hay caracteres multibyte.
Al migrar desde bases de datos Oracle que utilizan WE8MSWIN1252 o juegos de caracteres de un solo byte similares, verifique explícitamente las longitudes de las columnas después de la conversión.

3. BOOLEAN antes de Oracle 23c.
Oracle no tuvo un tipo de dato BOOLEAN nativo en SQL antes de la versión 23c (lanzada en 2023).
Las soluciones alternativas comunes eran NUMBER(1) con una restricción CHECK (0 para falso, 1 para verdadero) o CHAR(1) (‘Y’/‘N’).
Ninguno es detectado automáticamente como candidato a booleano por ora2pg.
Tendrá que identificar manualmente estas columnas y configurar MODIFICAR_TIPO para mapearlos a BOOLEAN de PostgreSQL, además de actualizar cualquier lógica de aplicación que inserte valores 0/1 o ‘Y’/‘N’.

4. ROWID y UROWID.
Las aplicaciones de Oracle a veces utilizan ROWID para una rápida recuperación de filas: almacene el ROWID de una fila y luego úselo para recuperar esa fila rápidamente más tarde.
El identificador de fila físico de PostgreSQL (ctid) no es estable.
Cambia cuando se actualiza una fila, cuando se ejecuta VACUUM FULL o cuando se agrupa una tabla.
Cualquier lógica de aplicación que almacene y reutilice ROWIDs debe ser rediseñada con una clave primaria adecuada.

Semántica de almacenamiento de TIMESTAMP WITH TIME ZONE.
Oracle TIMESTAMP WITH TIME ZONE almacena el desfase original (por ejemplo, 2024-03-15 14:30:00 +02:00).
TIMESTAMPTZ de PostgreSQL siempre se convierte a UTC internamente y aplica la zona horaria de la sesión al mostrar.
El valor aparece idéntico en la salida, pero la representación interna difiere.
Esto es importante cuando su aplicación lee los desfases horarios directamente de la base de datos, o cuando compara valores entre sesiones con diferentes configuraciones de zona horaria.
Prueba explícitamente las consultas sensibles a la zona horaria.


¿No estás seguro de cuáles de estas trampas se aplican a tu esquema?
Ofrezco una evaluación de migración de tarifa fija que revisa sus tipos de datos, el volumen de PL/SQL y las dependencias SQL de la aplicación, y entrega un registro de riesgos escrito antes de que comience cualquier trabajo de migración.
Ver qué cubre la evaluación


¿Qué maneja ora2pg automáticamente?

ora2pg mapea correctamente DATE a TIMESTAMP, CLOB y NCLOB a TEXT, BLOB y RAW a BYTEA, BINARY_FLOAT a REAL, BINARY_DOUBLE a DOUBLE PRECISION, y XMLTYPE a XML.
Lo que no maneja: optimización de NÚMEROS a tipos enteros, detección de columnas booleanas, diferencias de codificación VARCHAR2 y reescrituras de DBMS_LOB.
Estos requieren configuración manual de MODIFY_TYPE.

Lo que ora2pg no maneja automáticamente:

  • NUMERO a tipos enteros. Cada columna de NUMERO por defecto es NUMERIC. Debes usar la MODIFICAR_TIPO directiva para anular a nivel de columna.
  • Detección booleana. Las columnas NUMBER(1) y CHAR(1) usadas como booleanos no se detectan automáticamente. Se requiere configuración manual.
  • Semántica de bytes frente a semántica de caracteres VARCHAR2. ora2pg no ajusta las longitudes de las columnas por diferencias de codificación. Se requiere revisión manual para bases de datos de origen no UTF8.
  • Columnas ROWID. ora2pg marca estos, pero no los reemplaza. Se requiere rediseño manual.
  • Llamadas DBMS_LOB en PL/SQL. ora2pg convierte la estructura PL/SQL pero no reescribe las llamadas a la API LOB. Estas requieren reemplazo manual.

En documentación oficial de ora2pg cubre la referencia completa de configuración.

En TIPO_DE_DATO la directiva permite la reasignación de tipos global.

MODIFICAR_TIPO permite sobrescrituras a nivel de columna.


La lista de verificación para la decisión del tipo de datos

Antes de ejecutar la conversión de su esquema, revise esta lista:

  • Identificar cada columna Oracle DATE y confirmar que se mapea a TIMESTAMP
  • Consultar cada columna NUMÉRICA para determinar el rango y la escala de valores reales
  • Configurar las anulaciones de MODIFY_TYPE para columnas NUMBER(p,0) de rango entero
  • Identifica las columnas booleanas NUMBER(1) y CHAR(1) y configura MODIFY_TYPE
  • Compruebe el conjunto de caracteres de origen de Oracle para la semántica de bytes frente a caracteres de VARCHAR2
  • Auditar todo ES NULO verificar en el código de la aplicación el manejo de cadenas vacías
  • Enumerar cada uso de ROWID en el código de la aplicación y diseñar sustituciones de claves primarias
  • Identificar llamadas a DBMS_LOB en PL/SQL y planificar reescrituras
  • Pruebe las consultas de TIMESTAMP WITH TIME ZONE en diferentes configuraciones de zona horaria de sesión

Asegurar la asignación correcta de tipos antes de comenzar ahorra un importante trabajo de rehacer después de la migración de datos.

Una evaluación migratoria atrapa la mayoría de estos antes de que comience el proyecto.


Preguntas frecuentes

¿ora2pg maneja automáticamente todas las conversiones de tipos de datos de Oracle?

ora2pg maneja la mayoría de las conversiones comunes correctamente, incluyendo DATE a TIMESTAMP, CLOB a TEXT y BLOB a BYTEA.
No optimiza automáticamente NUMBER a tipos enteros, detecta columnas booleanas a partir de soluciones alternativas de NUMBER(1) o CHAR(1), ni ajusta las longitudes de VARCHAR2 para la semántica de bytes frente a caracteres.
Estos requieren configuración manual usando la directiva MODIFY_TYPE en ora2pg.conf.

TIMESTAMP

El equivalente correcto en PostgreSQL de Oracle DATE es TIMESTAMP, no DATE.
Oracle DATE almacena tanto la fecha como la hora (año, mes, día, hora, minuto, segundo).
PostgreSQL DATE almacena solo la fecha.
Mapear Oracle DATE a PostgreSQL DATE descarta silenciosamente el componente de hora de cada valor.
Este es uno de los problemas de corrupción de datos silenciosa más comunes en las migraciones de Oracle a PostgreSQL.

¿Cómo convierto eficientemente columnas Oracle NUMBER en PostgreSQL?

Analiza el rango y la escala de datos actuales para cada columna NUMÉRICA antes de mapear.
Para columnas con escala 0 (enteros) que quepan en 32 bits, utiliza INTEGER.
Para columnas dentro del rango de 64 bits, usa BIGINT.
Para precisión decimal fija, usa NUMERIC(p,s).
Reserve NUMERIC sin precisión para columnas que realmente requieran precisión arbitraria.
Usa la directiva MODIFY_TYPE de ora2pg para configurar las anulaciones a nivel de columna.
Establecer NUMERIC como predeterminado para todo es correcto, pero causa una degradación medible del rendimiento en cargas de trabajo de enteros.

¿Qué sucede con el tipo BOOLEAN de Oracle en PostgreSQL?

Oracle no tuvo un tipo BOOLEAN nativo de SQL antes de la versión 23c.
Los esquemas de Oracle anteriores a 23c típicamente usaban NUMBER(1) (0/1) o CHAR(1) (‘S’/‘N’) como soluciones alternativas para booleanos.
Estos no son convertidos automáticamente a BOOLEAN de PostgreSQL por las herramientas de migración.
Necesita identificar estas columnas manualmente, configurar MODIFY_TYPE en ora2pg para mapearlas a BOOLEAN y actualizar el código de la aplicación que inserta o lee valores 0/1 o Y/N.
Las bases de datos Oracle 23c con columnas BOOLEAN nativas se mapean directamente a BOOLEAN de PostgreSQL.

¿Puede PostgreSQL manejar datos CLOB de Oracle?

Sí.
PostgreSQL TEXT es la asignación correcta para Oracle CLOB.
TEXT de PostgreSQL no tiene un límite de tamaño fijo; los valores de hasta aproximadamente 1 GB se manejan automáticamente a través del almacenamiento TOAST.
La migración de datos en sí es sencilla.
El trabajo consiste en reescribir cualquier código de aplicación que utilice el paquete DBMS_LOB de Oracle (DBMS_LOB.READ, DBMS_LOB.WRITE, DBMS_LOB.GETLENGTH, etc.), ya que estas API LOB específicas de Oracle no tienen un equivalente directo en PostgreSQL y deben ser reemplazadas por funciones de cadena SQL estándar.


En resumen

El mapeo de tipos de datos de Oracle a PostgreSQL no es un ejercicio uno a uno.

La mayoría de los tipos se mapean limpiamente.

Un puñado — FECHA, NÚMERO, cadenas vacías, soluciones alternativas BOOLEAN y ROWID — requiere decisiones deliberadas y configuración manual.

Las dos reglas que evitan la mayoría de los problemas de datos posteriores a la migración:

  • Nunca mapees Oracle DATE a PostgreSQL DATE. Siempre usa TIMESTAMP.
  • Nunca aceptes NUMERIC para todas las columnas NUMBER. Analiza rangos y utiliza tipos enteros cuando sea apropiado.

Si planeas una migración de Oracle a PostgreSQL y quieres acertar con la estrategia de tipos de datos desde el principio, ponerse en contacto


Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *