Convierte Oracle a PostgreSQL
Suelta el archivo .sql que escribe Database Export de SQL Developer o SQLcl, con sus tablas y sentencias Insert, y descarga un script de PostgreSQL con las mismas tablas, claves, índices y datos. Cada valor que PostgreSQL no puede guardar tal cual, y cada objeto que no admite, aparece en el informe.
18 filas convertidas, sin correcciones, ninguna rechazada
Tablas en shop_2024_oracle.sqlVolcado de Oracle · escrito por SQL Developer · utf-8
customers8 filas
7 columnas id bigint not null name varchar(100) not null email varchar(150) not null country varchar(2) not null is_active smallint not null signed_up timestamp(0) created_at timestamp(0) not null orders10 filas
9 columnas id bigint not null customer_id bigint not null invoice bigint status varchar(10) not null total numeric(10,2) not null items integer not null ordered_at timestamp(0) not null shipped_at timestamp(0) note varchar(255)
-- Written by IntoSQL from a Oracle dump. Load it with: psql -v ON_ERROR_STOP=1 -f <file>
SET client_encoding = 'UTF8';
SET standard_conforming_strings = on;
SET check_function_bodies = false;
SET client_min_messages = warning;
SET synchronous_commit = off;
SET maintenance_work_mem = '512MB';
CREATE SEQUENCE invoice_numbers INCREMENT BY 1 MINVALUE 1 START WITH 1020;
CREATE TABLE customers (
id bigint GENERATED BY DEFAULT AS IDENTITY NOT NULL,
name varchar(100) NOT NULL,
email varchar(150) NOT NULL,
country varchar(2) DEFAULT 'US' NOT NULL,
is_active smallint DEFAULT 1 NOT NULL,
signed_up timestamp(0),
created_at timestamp(0) DEFAULT LOCALTIMESTAMP NOT NULL,
CONSTRAINT customers_check1 CHECK (is_active IN (0, 1))
);
CREATE TABLE orders (
id bigint GENERATED BY DEFAULT AS IDENTITY NOT NULL,
customer_id bigint NOT NULL,
invoice bigint DEFAULT (nextval('invoice_numbers')),
status varchar(10) DEFAULT 'pending' NOT NULL,
total numeric(10,2) NOT NULL,
items integer DEFAULT 1 NOT NULL,
ordered_at timestamp(0) NOT NULL,
shipped_at timestamp(0),
note varchar(255),
CONSTRAINT orders_check1 CHECK (status IN ('pending', 'paid', 'shipped', 'cancelled'))
);
COPY customers (id, name, email, country, is_active, signed_up, created_at) FROM stdin;
1 Ana Martínez ana@example.com MX 1 2023-01-15 00:00:00 2023-01-15 10:24:00.000000
2 Zoë Müller zoe@example.de DE 1 2023-02-03 00:00:00 2023-02-03 08:11:42.000000
3 José Peña jose@example.es ES 0 2023-03-21 00:00:00 2023-03-21 19:05:17.000000
4 李雷 lilei@example.cn CN 1 2023-05-09 00:00:00 2023-05-09 02:40:00.000000
5 Noah O'Brien noah@example.ie IE 1 \N 2023-06-30 14:00:00.000000
6 Amélie Laurent amelie@example.fr FR 1 2024-01-02 00:00:00 2024-01-02 09:30:00.000000
7 Ravi Patel ravi@example.in IN 0 2024-02-29 00:00:00 2024-02-29 23:59:59.000000
8 Sofia Rossi 🌻 sofia@example.it IT 1 2024-04-12 00:00:00 2024-04-12 12:12:12.000000
\.
COPY orders (id, customer_id, invoice, status, total, items, ordered_at, shipped_at, note) FROM stdin;
1 1 1000 shipped 129.9 3 2024-03-01 10:00:00 2024-03-02 16:30:00 \N
2 2 1001 paid 54 1 2024-03-02 11:15:00 \N Gift wrap 🎁
3 1 1002 cancelled 12.5 1 2024-03-03 09:45:00 \N Customer changed their mind
4 4 1003 shipped 980 12 2024-03-05 02:10:00 2024-03-07 08:00:00 请小心轻放
5 5 1004 pending 45.75 2 2024-03-08 17:20:00 \N Leave at the door; ring twice
6 6 1005 shipped 310.4 5 2024-03-10 13:00:00 2024-03-11 09:00:00 Fragile: "glass"
7 8 1006 paid 76.2 2 2024-03-11 18:45:00 \N \N
8 2 1007 paid 19.99 1 2024-03-12 08:00:00 \N Paid at the counter
9 3 1008 shipped 64 2 2024-03-14 15:30:00 \N Shipping date never recorded
10 7 1009 paid 1499 1 2024-03-15 21:05:00 \N C:\\Users\\ravi\\invoice.pdf
\.
-- Keys, indexes and constraints, after the rows so each builds once.
CREATE UNIQUE INDEX customers_customers_pk ON customers (id);
CREATE UNIQUE INDEX customers_customers_email_uk ON customers (email);
CREATE UNIQUE INDEX orders_orders_pk ON orders (id);
CREATE INDEX orders_orders_status_date_ix ON orders (status, ordered_at);
ALTER TABLE customers ADD CONSTRAINT customers_customers_pk PRIMARY KEY USING INDEX customers_customers_pk;
ALTER TABLE customers ADD CONSTRAINT customers_customers_email_uk UNIQUE USING INDEX customers_customers_email_uk;
ALTER TABLE orders ADD CONSTRAINT orders_orders_pk PRIMARY KEY USING INDEX orders_orders_pk;
ALTER TABLE orders ADD CONSTRAINT orders_customer_fk FOREIGN KEY (customer_id) REFERENCES customers (id) NOT VALID;
DO $f$ BEGIN
ALTER TABLE orders VALIDATE CONSTRAINT orders_customer_fk;
EXCEPTION WHEN foreign_key_violation THEN
RAISE WARNING 'foreign key % has rows without a parent: it stays NOT VALID, and new rows are checked', 'orders_customer_fk';
END $f$;
SELECT setval(pg_get_serial_sequence('customers', 'id'), LEAST(GREATEST(COALESCE((SELECT max(id) FROM customers)::numeric, 0) + 1, 1), 9223372036854775807)::bigint, false);
COMMENT ON TABLE customers IS 'People who bought at least once';
SELECT setval(pg_get_serial_sequence('orders', 'id'), LEAST(GREATEST(COALESCE((SELECT max(id) FROM orders)::numeric, 0) + 1, 1), 9223372036854775807)::bigint, false);
Correcciones
No hubo nada que corregir.
Tu archivo se convierte en esta computadora y nunca se sube.
Otros ejemplos:
Qué cambia de Oracle a PostgreSQL
Cada fila es una regla que aplica el convertidor, con la línea que escribe en el informe cuando la regla cambia algo en tus datos.
BOOLEAN (23ai)
boolean
TRUE y FALSE siguen siendo true y false.
VARCHAR2(n BYTE|CHAR), NVARCHAR2(n), CHAR(n), NCHAR(n), CLOB, NCLOB, LONG
varchar(n) y text
Las longitudes cuentan caracteres, tanto si Oracle contaba bytes como caracteres, así que cada valor cabe.
NUMBER, NUMBER(p,s), FLOAT(b)
numeric(p,s), o bigint
NUMBER conserva cada dígito; un NUMBER sin precisión cuyos valores son todos números enteros de hasta 18 dígitos es un entero de 64 bits, y una escala negativa admite otros tantos dígitos antes del punto.
DATE, TIMESTAMP(p)
timestamp(p)
El DATE de Oracle guarda una hora al segundo, así que es una fecha y hora. Una exportación en una sesión estadounidense o alemana escribe las fechas sin su hora ni su siglo (DD-MON-RR); exporta en una sesión ISO (SYYYY-MM-DD HH24:MI:SS) para conservarlas.
TIMESTAMP(7) to TIMESTAMP(9)
seis dígitos de segundo, el séptimo se corta
PostgreSQL guarda microsegundos; el séptimo dígito (100 nanosegundos) se corta, nunca se redondea, así que ningún valor pasa al segundo o al día siguiente.
Línea del informe: fraction_cut
TIMESTAMP WITH TIME ZONE, TIMESTAMP WITH LOCAL TIME ZONE
timestamptz, los mismos instantes
timestamptz conserva el instante; PostgreSQL lo muestra en la zona horaria de la sesión en lugar del desfase que tenía cada valor.
JSON (21c), and CLOB holding JSON
jsonb
Los mismos valores JSON; un NULL de SQL que la exportación escribió como 'null' sigue siendo NULL.
BLOB, RAW(n), LONG RAW
bytea
Los bytes siguen siendo bytes.
ROWID, UROWID, XMLTYPE, SDO_GEOMETRY
text, y xml para XMLTYPE
Los tipos propios de Oracle no tienen equivalente; sus valores conservan su texto, incluidos los valores ROWID, aunque fuera de Oracle no signifiquen nada.
Línea del informe: type_replaced
collations that ignore case (BINARY_CI)
comparación exacta
PostgreSQL compara el texto exactamente, así que las columnas que no distinguían mayúsculas en Oracle ahora distinguen Ana de ana.
Línea del informe: case_insensitive_collation
identity columns, and a sequence a BEFORE INSERT trigger assigns
GENERATED BY DEFAULT AS IDENTITY, siguiendo después del id más alto
Los ids copiados se cargan tal cual, y el contador sigue después del más alto. Una columna que un disparador llena desde una secuencia (la forma anterior a 12c) también es de identidad.
CREATE SEQUENCE
CREATE SEQUENCE con las mismas opciones
La exportación escribe hasta dónde llegó una secuencia como su START WITH, así que sigue desde ahí.
virtual columns (GENERATED ALWAYS AS ... VIRTUAL)
columnas generadas, STORED
PostgreSQL 13 a 17 solo guardan columnas generadas; la fórmula es la misma, con || escrito para que NULL cuente como vacío, como en Oracle.
Línea del informe: virtual_as_stored
virtual columns with functions IntoSQL doesn't write, and the || operator
una columna simple
Una fórmula que no se puede escribir para PostgreSQL deja una columna simple y una línea en el informe; los valores que la exportación escribió para las columnas virtuales se omiten, y PostgreSQL no tiene ninguno que calcular.
Línea del informe: generated_not_translated
defaults with functions IntoSQL doesn't write (SYS_GUID(), ||)
sin valor predeterminado
Un valor predeterminado que no se puede escribir para PostgreSQL se deja fuera y aparece en el informe.
Línea del informe: default_not_translated
CHECK constraints
traducidos
Los mismos CHECK, comprobados al cargar cada fila.
CHECK constraints with functions IntoSQL doesn't write
no se crea
Un CHECK que no se puede escribir para PostgreSQL se deja fuera y aparece en el informe.
Línea del informe: check_skipped
CHECK constraints that are DISABLE or ENABLE NOVALIDATE
no se crea
Oracle no aplica estos CHECK a las filas que ya estaban, así que las filas pueden no cumplirlos.
Línea del informe: check_not_enforced
function-based, bitmap and domain indexes
no se crea
IntoSQL escribe índices b-tree sobre columnas; vuelve a crear los demás después de cargar.
Línea del informe: index_skipped
foreign keys
claves foráneas, después de las filas
Las filas cargan en cualquier orden, y después las claves las comprueban.
foreign keys to tables the export doesn't create, and DISABLE or NOVALIDATE ones
no se crea
Una clave no puede nombrar una tabla que no existe, y una que Oracle no comprueba puede tener filas sin padre.
Línea del informe: foreign_key_skipped
partitioned tables
una tabla con las filas de todas las particiones
Las filas son las mismas; vuelve a particionar la tabla en PostgreSQL si lo necesita.
Línea del informe: partitions_dropped
NOT NULL columns with values that become NULL
sin NOT NULL
Una columna cuyos valores tuvieron que volverse NULL no puede conservar NOT NULL, o la carga se detendría.
Línea del informe: not_null_relaxed
views, synonyms, types, packages, procedures, functions and other triggers
listados en el informe
Su código está escrito en PL/SQL, así que se listan para que los vuelvas a crear.
Línea del informe: not_converted_objects
exports without CREATE TABLE (data only)
tipos leídos de los valores
Una exportación solo de datos solo tiene filas, así que los tipos salen de los valores.
Línea del informe: no_schema
expressions in INSERT values other than the ones exports write
NULL
Un convertidor que no ejecuta una base de datos no puede evaluarlas; sí se leen to_date, to_timestamp, TO_CLOB, HEXTORAW y las demás que escriben las exportaciones.
Línea del informe: expression_value
types IntoSQL doesn't know, and object types
text
El texto admite cualquier valor; cambia el tipo después de cargar.
Línea del informe: unknown_type
dates before year 1 and time zones given as region names
NULL
Un valor que no es una fecha se vuelve NULL y el informe lo cuenta.
Línea del informe: date_out_of_range
NaN and Infinity in BINARY_FLOAT and BINARY_DOUBLE
NaN e Infinity se conservan
Los tipos de coma flotante de PostgreSQL admiten NaN e infinito.
NUL characters in text
se quita
El texto de PostgreSQL no puede contener un carácter NUL. Se quita y el informe cuenta los valores.
Línea del informe: nul_removed
text that isn't valid in its encoding
los bytes incorrectos como carácter de reemplazo
El texto de PostgreSQL tiene que ser UTF-8 válido.
Línea del informe: invalid_utf8_replaced
text decoded twice (ñ)
se copia tal cual
El script contiene exactamente estos caracteres, así que se copian; el informe los señala.
Línea del informe: mojibake_suspected
| En Oracle | En PostgreSQL | Línea del informe |
|---|---|---|
| BOOLEAN (23ai) | booleanTRUE y FALSE siguen siendo true y false. | |
| VARCHAR2(n BYTE|CHAR), NVARCHAR2(n), CHAR(n), NCHAR(n), CLOB, NCLOB, LONG | varchar(n) y textLas longitudes cuentan caracteres, tanto si Oracle contaba bytes como caracteres, así que cada valor cabe. | |
| NUMBER, NUMBER(p,s), FLOAT(b) | numeric(p,s), o bigintNUMBER conserva cada dígito; un NUMBER sin precisión cuyos valores son todos números enteros de hasta 18 dígitos es un entero de 64 bits, y una escala negativa admite otros tantos dígitos antes del punto. | |
| DATE, TIMESTAMP(p) | timestamp(p)El DATE de Oracle guarda una hora al segundo, así que es una fecha y hora. Una exportación en una sesión estadounidense o alemana escribe las fechas sin su hora ni su siglo (DD-MON-RR); exporta en una sesión ISO (SYYYY-MM-DD HH24:MI:SS) para conservarlas. | |
| TIMESTAMP(7) to TIMESTAMP(9) | seis dígitos de segundo, el séptimo se cortaPostgreSQL guarda microsegundos; el séptimo dígito (100 nanosegundos) se corta, nunca se redondea, así que ningún valor pasa al segundo o al día siguiente. | fraction_cut |
| TIMESTAMP WITH TIME ZONE, TIMESTAMP WITH LOCAL TIME ZONE | timestamptz, los mismos instantestimestamptz conserva el instante; PostgreSQL lo muestra en la zona horaria de la sesión en lugar del desfase que tenía cada valor. | |
| JSON (21c), and CLOB holding JSON | jsonbLos mismos valores JSON; un NULL de SQL que la exportación escribió como 'null' sigue siendo NULL. | |
| BLOB, RAW(n), LONG RAW | byteaLos bytes siguen siendo bytes. | |
| ROWID, UROWID, XMLTYPE, SDO_GEOMETRY | text, y xml para XMLTYPELos tipos propios de Oracle no tienen equivalente; sus valores conservan su texto, incluidos los valores ROWID, aunque fuera de Oracle no signifiquen nada. | type_replaced |
| collations that ignore case (BINARY_CI) | comparación exactaPostgreSQL compara el texto exactamente, así que las columnas que no distinguían mayúsculas en Oracle ahora distinguen Ana de ana. | case_insensitive_collation |
| identity columns, and a sequence a BEFORE INSERT trigger assigns | GENERATED BY DEFAULT AS IDENTITY, siguiendo después del id más altoLos ids copiados se cargan tal cual, y el contador sigue después del más alto. Una columna que un disparador llena desde una secuencia (la forma anterior a 12c) también es de identidad. | |
| CREATE SEQUENCE | CREATE SEQUENCE con las mismas opcionesLa exportación escribe hasta dónde llegó una secuencia como su START WITH, así que sigue desde ahí. | |
| virtual columns (GENERATED ALWAYS AS ... VIRTUAL) | columnas generadas, STOREDPostgreSQL 13 a 17 solo guardan columnas generadas; la fórmula es la misma, con || escrito para que NULL cuente como vacío, como en Oracle. | virtual_as_stored |
| virtual columns with functions IntoSQL doesn't write, and the || operator | una columna simpleUna fórmula que no se puede escribir para PostgreSQL deja una columna simple y una línea en el informe; los valores que la exportación escribió para las columnas virtuales se omiten, y PostgreSQL no tiene ninguno que calcular. | generated_not_translated |
| defaults with functions IntoSQL doesn't write (SYS_GUID(), ||) | sin valor predeterminadoUn valor predeterminado que no se puede escribir para PostgreSQL se deja fuera y aparece en el informe. | default_not_translated |
| CHECK constraints | traducidosLos mismos CHECK, comprobados al cargar cada fila. | |
| CHECK constraints with functions IntoSQL doesn't write | no se creaUn CHECK que no se puede escribir para PostgreSQL se deja fuera y aparece en el informe. | check_skipped |
| CHECK constraints that are DISABLE or ENABLE NOVALIDATE | no se creaOracle no aplica estos CHECK a las filas que ya estaban, así que las filas pueden no cumplirlos. | check_not_enforced |
| function-based, bitmap and domain indexes | no se creaIntoSQL escribe índices b-tree sobre columnas; vuelve a crear los demás después de cargar. | index_skipped |
| foreign keys | claves foráneas, después de las filasLas filas cargan en cualquier orden, y después las claves las comprueban. | |
| foreign keys to tables the export doesn't create, and DISABLE or NOVALIDATE ones | no se creaUna clave no puede nombrar una tabla que no existe, y una que Oracle no comprueba puede tener filas sin padre. | foreign_key_skipped |
| partitioned tables | una tabla con las filas de todas las particionesLas filas son las mismas; vuelve a particionar la tabla en PostgreSQL si lo necesita. | partitions_dropped |
| NOT NULL columns with values that become NULL | sin NOT NULLUna columna cuyos valores tuvieron que volverse NULL no puede conservar NOT NULL, o la carga se detendría. | not_null_relaxed |
| views, synonyms, types, packages, procedures, functions and other triggers | listados en el informeSu código está escrito en PL/SQL, así que se listan para que los vuelvas a crear. | not_converted_objects |
| exports without CREATE TABLE (data only) | tipos leídos de los valoresUna exportación solo de datos solo tiene filas, así que los tipos salen de los valores. | no_schema |
| expressions in INSERT values other than the ones exports write | NULLUn convertidor que no ejecuta una base de datos no puede evaluarlas; sí se leen to_date, to_timestamp, TO_CLOB, HEXTORAW y las demás que escriben las exportaciones. | expression_value |
| types IntoSQL doesn't know, and object types | textEl texto admite cualquier valor; cambia el tipo después de cargar. | unknown_type |
| dates before year 1 and time zones given as region names | NULLUn valor que no es una fecha se vuelve NULL y el informe lo cuenta. | date_out_of_range |
| NaN and Infinity in BINARY_FLOAT and BINARY_DOUBLE | NaN e Infinity se conservanLos tipos de coma flotante de PostgreSQL admiten NaN e infinito. | |
| NUL characters in text | se quitaEl texto de PostgreSQL no puede contener un carácter NUL. Se quita y el informe cuenta los valores. | nul_removed |
| text that isn't valid in its encoding | los bytes incorrectos como carácter de reemplazoEl texto de PostgreSQL tiene que ser UTF-8 válido. | invalid_utf8_replaced |
| text decoded twice (ñ) | se copia tal cualEl script contiene exactamente estos caracteres, así que se copian; el informe los señala. | mojibake_suspected |
Carga el script en PostgreSQL
El script crea las tablas, copia las filas y luego agrega claves, índices y claves foráneas, así que carga rápido y se detiene en el primer error.
psql -v ON_ERROR_STOP=1 -d mydb -f oracle.postgresql.sqlLas claves foráneas se agregan como NOT VALID y luego se validan. Si una fila apunta a una fila que no existe, el script te dice qué restricción es, en lugar de fallar a medias.
Para un volcado grande, crea primero la base de datos y ejecuta el script con psql desde la misma máquina que el servidor.
IntoSQL Pro carga el archivo directo en PostgreSQL desde la app de escritorio o la línea de comandos, sin escribir un script.
Haz el volcado de Oracle para el mejor resultado
El convertidor lee lo que contiene el volcado. Estas opciones conservan más de tus datos en él:
- En SQL Developer, elige Tools, Database Export, con DDL y el formato de datos Insert. SQLcl escribe lo mismo con SET SQLFORMAT INSERT.
- Ejecuta ALTER SESSION SET NLS_DATE_FORMAT = 'SYYYY-MM-DD HH24:MI:SS' antes de exportar: el formato predeterminado escribe las fechas sin su hora ni su siglo.
- Un archivo .dmp de exp o expdp es binario: impórtalo en una base de datos Oracle, por ejemplo Oracle Database Free en Docker, y expórtalo con SQL Developer.
Qué no se convierte
Las vistas, paquetes, procedimientos, funciones, disparadores, tipos y sinónimos se escriben en PL/SQL. Aparecen en el informe por nombre, nunca se descartan en silencio, para que puedas reescribirlos para PostgreSQL.
Preguntas sobre Oracle a PostgreSQL
¿No encuentras la respuesta? Escríbenos desde la página de contacto y te responde una persona. Página de contacto
¿Se sube mi volcado?
No. El convertidor se ejecuta en tu navegador y lee el volcado de tu disco por partes, así que nunca sale de tu computadora. La app de escritorio y la línea de comandos también convierten en tu computadora.
¿Qué tan grande puede ser el volcado?
El convertidor nunca tiene todo el volcado en memoria: lee el archivo dos veces, por rangos, y escribe el script sobre la marcha. Los volcados de varios gigabytes se convierten en el navegador, en la app de escritorio y con la línea de comandos.
¿Puedo convertir un archivo .dmp de exp o expdp?
No directamente: Data Pump y el Export original escriben un formato binario que solo Oracle lee. Impórtalo en cualquier base de datos Oracle, Oracle Database Free funciona en Docker, y expórtalo con Database Export de SQL Developer o con SQLcl. El convertidor te lo indica cuando recibe un archivo .dmp.
¿De qué versiones de Oracle puede venir la exportación?
Exportaciones de SQL Developer y SQLcl, con las definiciones de tabla de DBMS_METADATA, desde Oracle 11g en adelante. Cada exportación de prueba viene de Oracle 21c y 23ai, en sesiones estadounidenses, alemanas e ISO, y se compara con su origen celda por celda.
¿Qué pasa con NUMBER, DATE y los demás tipos de Oracle?
NUMBER se vuelve numeric, o bigint cuando cada valor es un número entero; DATE se vuelve timestamp, conservando su hora; CLOB text, BLOB y RAW bytea, BINARY_DOUBLE double precision con NaN e Infinity, y los intervalos interval.
¿Se conservan las columnas de identidad y las secuencias?
Sí. Las columnas de identidad siguen siendo columnas de identidad y siguen después del id más alto, y lo mismo pasa con una columna que un disparador llena desde una secuencia. Las secuencias siguen desde donde la exportación dice que llegaron.
¿Qué pasa con los paquetes, procedimientos y disparadores de PL/SQL?
Aparecen en el informe por nombre y no se convierten, porque cada base de datos los escribe en su propio lenguaje.
¿En qué versiones de PostgreSQL carga el script?
PostgreSQL 13 a 18. El mismo script carga en cada una, y se detiene en el primer error si algo sale mal.