Converti Oracle in PostgreSQL
Trascina il file .sql che scrive Database Export di SQL Developer o SQLcl, con le sue tabelle e istruzioni Insert, e scarica uno script per PostgreSQL con le stesse tabelle, chiavi, indici e dati. Ogni valore che PostgreSQL non può contenere così com'è, e ogni oggetto che non supporta, è elencato nel report.
18 righe convertite, nessuna correzione, nessuna rifiutata
Tabelle in shop_2024_oracle.sqlDump Oracle · scritto da SQL Developer · utf-8
customers8 righe
7 colonne 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 righe
9 colonne 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);
Correzioni
Non c'era niente da correggere.
Il tuo file viene convertito su questo computer e non viene mai caricato.
Altri esempi:
Cosa cambia da Oracle a PostgreSQL
Ogni riga è una regola che il convertitore applica, con la riga del report che scrive quando la regola cambia qualcosa nei tuoi dati.
BOOLEAN (23ai)
boolean
TRUE e FALSE restano true e false.
VARCHAR2(n BYTE|CHAR), NVARCHAR2(n), CHAR(n), NCHAR(n), CLOB, NCLOB, LONG
varchar(n) e text
Le lunghezze contano i caratteri, sia che Oracle contasse byte sia caratteri, quindi ogni valore ci sta.
NUMBER, NUMBER(p,s), FLOAT(b)
numeric(p,s), o bigint
NUMBER mantiene ogni cifra; un NUMBER senza precisione i cui valori sono tutti numeri interi fino a 18 cifre è un intero a 64 bit, e una scala negativa contiene altrettante cifre prima della virgola.
DATE, TIMESTAMP(p)
timestamp(p)
Il DATE di Oracle contiene un'ora al secondo, quindi è una data e ora. Un export in una sessione americana o tedesca scrive le date senza ora né secolo (DD-MON-RR); esporta in una sessione ISO (SYYYY-MM-DD HH24:MI:SS) per mantenerle.
TIMESTAMP(7) to TIMESTAMP(9)
sei cifre di secondo, la settima troncata
PostgreSQL mantiene i microsecondi; la settima cifra (100 nanosecondi) viene troncata, mai arrotondata, quindi nessun valore passa al secondo o al giorno successivo.
Riga del report: fraction_cut
TIMESTAMP WITH TIME ZONE, TIMESTAMP WITH LOCAL TIME ZONE
timestamptz, gli stessi istanti
timestamptz mantiene l'istante; PostgreSQL lo mostra nel fuso orario della sessione invece che con l'offset di ogni valore.
JSON (21c), and CLOB holding JSON
jsonb
Gli stessi valori JSON; un NULL SQL che l'export ha scritto come 'null' resta NULL.
BLOB, RAW(n), LONG RAW
bytea
I byte restano byte.
ROWID, UROWID, XMLTYPE, SDO_GEOMETRY
text, e xml per XMLTYPE
I tipi propri di Oracle non hanno un equivalente; i loro valori mantengono il testo, valori ROWID inclusi, anche se fuori da Oracle non significano nulla.
Riga del report: type_replaced
collations that ignore case (BINARY_CI)
confronto esatto
PostgreSQL confronta il testo in modo esatto, quindi le colonne che in Oracle ignoravano le maiuscole ora distinguono Ana da ana.
Riga del report: case_insensitive_collation
identity columns, and a sequence a BEFORE INSERT trigger assigns
GENERATED BY DEFAULT AS IDENTITY, che riparte dopo l'id più alto
Gli id copiati si caricano così come sono, e il contatore riparte dopo il più alto. Anche una colonna che un trigger riempie da una sequenza (il metodo prima della 12c) è una identity.
CREATE SEQUENCE
CREATE SEQUENCE con le stesse opzioni
L'export scrive fin dove è arrivata una sequenza come suo START WITH, quindi riparte da lì.
virtual columns (GENERATED ALWAYS AS ... VIRTUAL)
colonne generate, STORED
PostgreSQL dalla 13 alla 17 salva solo colonne generate STORED; la formula è la stessa, con || scritto in modo che NULL valga come vuoto come in Oracle.
Riga del report: virtual_as_stored
virtual columns with functions IntoSQL doesn't write, and the || operator
una colonna semplice
Una formula che non si può scrivere per PostgreSQL lascia una colonna semplice e una riga nel report; i valori che l'export ha scritto per le colonne virtuali vengono omessi, e PostgreSQL non ne ha nessuno da calcolare.
Riga del report: generated_not_translated
defaults with functions IntoSQL doesn't write (SYS_GUID(), ||)
nessun valore predefinito
Un valore predefinito che non si può scrivere per PostgreSQL viene omesso e segnalato.
Riga del report: default_not_translated
CHECK constraints
tradotti
Gli stessi vincoli CHECK, verificati mentre si carica ogni riga.
CHECK constraints with functions IntoSQL doesn't write
non creato
Un vincolo CHECK che non si può scrivere per PostgreSQL viene omesso e segnalato.
Riga del report: check_skipped
CHECK constraints that are DISABLE or ENABLE NOVALIDATE
non creato
Oracle non applica questi vincoli CHECK alle righe già presenti, quindi le righe potrebbero violarli.
Riga del report: check_not_enforced
function-based, bitmap and domain indexes
non creato
IntoSQL scrive indici b-tree su colonne; ricrea gli altri dopo il caricamento.
Riga del report: index_skipped
foreign keys
chiavi esterne, dopo le righe
Le righe si caricano in qualsiasi ordine, e le chiavi le verificano dopo.
foreign keys to tables the export doesn't create, and DISABLE or NOVALIDATE ones
non creato
Una chiave non può riferirsi a una tabella che non c'è, e una che Oracle non verifica può avere righe senza padre.
Riga del report: foreign_key_skipped
partitioned tables
una tabella con le righe di ogni partizione
Le righe sono le stesse; partiziona di nuovo la tabella in PostgreSQL se serve.
Riga del report: partitions_dropped
NOT NULL columns with values that become NULL
NOT NULL rimosso
Una colonna i cui valori sono dovuti diventare NULL non può mantenere NOT NULL, altrimenti il caricamento si fermerebbe.
Riga del report: not_null_relaxed
views, synonyms, types, packages, procedures, functions and other triggers
elencati nel report
Il loro codice è scritto in PL/SQL, quindi vengono elencati perché tu li ricrei.
Riga del report: not_converted_objects
exports without CREATE TABLE (data only)
tipi ricavati dai valori
Un export di soli dati contiene solo righe, quindi i tipi vengono ricavati dai valori.
Riga del report: no_schema
expressions in INSERT values other than the ones exports write
NULL
Un convertitore che non esegue un database non può valutarle; to_date, to_timestamp, TO_CLOB, HEXTORAW e le altre che scrivono gli export vengono lette.
Riga del report: expression_value
types IntoSQL doesn't know, and object types
text
Il testo contiene qualsiasi valore; cambia il tipo dopo il caricamento.
Riga del report: unknown_type
dates before year 1 and time zones given as region names
NULL
Un valore che non è una data diventa NULL e il report lo conta.
Riga del report: date_out_of_range
NaN and Infinity in BINARY_FLOAT and BINARY_DOUBLE
NaN e Infinity mantenuti
I tipi a virgola mobile di PostgreSQL contengono NaN e infinito.
NUL characters in text
rimosso
Il testo di PostgreSQL non può contenere il carattere NUL. Viene rimosso e il report conta i valori.
Riga del report: nul_removed
text that isn't valid in its encoding
i byte errati come carattere di sostituzione
Il testo di PostgreSQL deve essere UTF-8 valido.
Riga del report: invalid_utf8_replaced
text decoded twice (ñ)
copiato così com'è
Lo script contiene esattamente questi caratteri, quindi vengono copiati; il report li segnala.
Riga del report: mojibake_suspected
| In Oracle | In PostgreSQL | Riga del report |
|---|---|---|
| BOOLEAN (23ai) | booleanTRUE e FALSE restano true e false. | |
| VARCHAR2(n BYTE|CHAR), NVARCHAR2(n), CHAR(n), NCHAR(n), CLOB, NCLOB, LONG | varchar(n) e textLe lunghezze contano i caratteri, sia che Oracle contasse byte sia caratteri, quindi ogni valore ci sta. | |
| NUMBER, NUMBER(p,s), FLOAT(b) | numeric(p,s), o bigintNUMBER mantiene ogni cifra; un NUMBER senza precisione i cui valori sono tutti numeri interi fino a 18 cifre è un intero a 64 bit, e una scala negativa contiene altrettante cifre prima della virgola. | |
| DATE, TIMESTAMP(p) | timestamp(p)Il DATE di Oracle contiene un'ora al secondo, quindi è una data e ora. Un export in una sessione americana o tedesca scrive le date senza ora né secolo (DD-MON-RR); esporta in una sessione ISO (SYYYY-MM-DD HH24:MI:SS) per mantenerle. | |
| TIMESTAMP(7) to TIMESTAMP(9) | sei cifre di secondo, la settima troncataPostgreSQL mantiene i microsecondi; la settima cifra (100 nanosecondi) viene troncata, mai arrotondata, quindi nessun valore passa al secondo o al giorno successivo. | fraction_cut |
| TIMESTAMP WITH TIME ZONE, TIMESTAMP WITH LOCAL TIME ZONE | timestamptz, gli stessi istantitimestamptz mantiene l'istante; PostgreSQL lo mostra nel fuso orario della sessione invece che con l'offset di ogni valore. | |
| JSON (21c), and CLOB holding JSON | jsonbGli stessi valori JSON; un NULL SQL che l'export ha scritto come 'null' resta NULL. | |
| BLOB, RAW(n), LONG RAW | byteaI byte restano byte. | |
| ROWID, UROWID, XMLTYPE, SDO_GEOMETRY | text, e xml per XMLTYPEI tipi propri di Oracle non hanno un equivalente; i loro valori mantengono il testo, valori ROWID inclusi, anche se fuori da Oracle non significano nulla. | type_replaced |
| collations that ignore case (BINARY_CI) | confronto esattoPostgreSQL confronta il testo in modo esatto, quindi le colonne che in Oracle ignoravano le maiuscole ora distinguono Ana da ana. | case_insensitive_collation |
| identity columns, and a sequence a BEFORE INSERT trigger assigns | GENERATED BY DEFAULT AS IDENTITY, che riparte dopo l'id più altoGli id copiati si caricano così come sono, e il contatore riparte dopo il più alto. Anche una colonna che un trigger riempie da una sequenza (il metodo prima della 12c) è una identity. | |
| CREATE SEQUENCE | CREATE SEQUENCE con le stesse opzioniL'export scrive fin dove è arrivata una sequenza come suo START WITH, quindi riparte da lì. | |
| virtual columns (GENERATED ALWAYS AS ... VIRTUAL) | colonne generate, STOREDPostgreSQL dalla 13 alla 17 salva solo colonne generate STORED; la formula è la stessa, con || scritto in modo che NULL valga come vuoto come in Oracle. | virtual_as_stored |
| virtual columns with functions IntoSQL doesn't write, and the || operator | una colonna sempliceUna formula che non si può scrivere per PostgreSQL lascia una colonna semplice e una riga nel report; i valori che l'export ha scritto per le colonne virtuali vengono omessi, e PostgreSQL non ne ha nessuno da calcolare. | generated_not_translated |
| defaults with functions IntoSQL doesn't write (SYS_GUID(), ||) | nessun valore predefinitoUn valore predefinito che non si può scrivere per PostgreSQL viene omesso e segnalato. | default_not_translated |
| CHECK constraints | tradottiGli stessi vincoli CHECK, verificati mentre si carica ogni riga. | |
| CHECK constraints with functions IntoSQL doesn't write | non creatoUn vincolo CHECK che non si può scrivere per PostgreSQL viene omesso e segnalato. | check_skipped |
| CHECK constraints that are DISABLE or ENABLE NOVALIDATE | non creatoOracle non applica questi vincoli CHECK alle righe già presenti, quindi le righe potrebbero violarli. | check_not_enforced |
| function-based, bitmap and domain indexes | non creatoIntoSQL scrive indici b-tree su colonne; ricrea gli altri dopo il caricamento. | index_skipped |
| foreign keys | chiavi esterne, dopo le righeLe righe si caricano in qualsiasi ordine, e le chiavi le verificano dopo. | |
| foreign keys to tables the export doesn't create, and DISABLE or NOVALIDATE ones | non creatoUna chiave non può riferirsi a una tabella che non c'è, e una che Oracle non verifica può avere righe senza padre. | foreign_key_skipped |
| partitioned tables | una tabella con le righe di ogni partizioneLe righe sono le stesse; partiziona di nuovo la tabella in PostgreSQL se serve. | partitions_dropped |
| NOT NULL columns with values that become NULL | NOT NULL rimossoUna colonna i cui valori sono dovuti diventare NULL non può mantenere NOT NULL, altrimenti il caricamento si fermerebbe. | not_null_relaxed |
| views, synonyms, types, packages, procedures, functions and other triggers | elencati nel reportIl loro codice è scritto in PL/SQL, quindi vengono elencati perché tu li ricrei. | not_converted_objects |
| exports without CREATE TABLE (data only) | tipi ricavati dai valoriUn export di soli dati contiene solo righe, quindi i tipi vengono ricavati dai valori. | no_schema |
| expressions in INSERT values other than the ones exports write | NULLUn convertitore che non esegue un database non può valutarle; to_date, to_timestamp, TO_CLOB, HEXTORAW e le altre che scrivono gli export vengono lette. | expression_value |
| types IntoSQL doesn't know, and object types | textIl testo contiene qualsiasi valore; cambia il tipo dopo il caricamento. | unknown_type |
| dates before year 1 and time zones given as region names | NULLUn valore che non è una data diventa NULL e il report lo conta. | date_out_of_range |
| NaN and Infinity in BINARY_FLOAT and BINARY_DOUBLE | NaN e Infinity mantenutiI tipi a virgola mobile di PostgreSQL contengono NaN e infinito. | |
| NUL characters in text | rimossoIl testo di PostgreSQL non può contenere il carattere NUL. Viene rimosso e il report conta i valori. | nul_removed |
| text that isn't valid in its encoding | i byte errati come carattere di sostituzioneIl testo di PostgreSQL deve essere UTF-8 valido. | invalid_utf8_replaced |
| text decoded twice (ñ) | copiato così com'èLo script contiene esattamente questi caratteri, quindi vengono copiati; il report li segnala. | mojibake_suspected |
Carica lo script in PostgreSQL
Lo script crea le tabelle, copia le righe, poi aggiunge chiavi, indici e chiavi esterne, così si carica in fretta e si ferma al primo errore.
psql -v ON_ERROR_STOP=1 -d mydb -f oracle.postgresql.sqlLe chiavi esterne vengono aggiunte come NOT VALID e poi validate. Se una riga punta a una riga che non esiste, lo script ti dice quale vincolo, invece di fallire a metà.
Per un dump grande, crea prima il database ed esegui lo script con psql dalla stessa macchina del server.
IntoSQL Pro carica il file direttamente in PostgreSQL dall'app desktop o dalla riga di comando, senza scrivere uno script.
Fai il dump di Oracle per il miglior risultato
Il convertitore legge ciò che il dump contiene. Con queste opzioni il dump conserva più dei tuoi dati:
- In SQL Developer, scegli Tools, Database Export, con DDL e il formato dati Insert. SQLcl scrive lo stesso con SET SQLFORMAT INSERT.
- Esegui ALTER SESSION SET NLS_DATE_FORMAT = 'SYYYY-MM-DD HH24:MI:SS' prima di esportare: il formato di default scrive le date senza ora né secolo.
- Un file .dmp di exp o expdp è binario: importalo in un database Oracle, per esempio Oracle Database Free in Docker, ed esportalo con SQL Developer.
Cosa non viene convertito
Viste, package, procedure, funzioni, trigger, tipi e sinonimi sono scritti in PL/SQL. Sono elencati per nome nel report, mai rimossi in silenzio, così puoi riscriverli per PostgreSQL.
Domande sulla conversione da Oracle a PostgreSQL
Non trovi la risposta? Scrivici dalla pagina dei contatti e ti risponde una persona. Pagina dei contatti
Il mio dump viene caricato?
No. Il convertitore funziona nel tuo browser e legge il dump dal tuo disco a pezzi, quindi non lascia mai il tuo computer. Anche l'app desktop e la riga di comando convertono sul tuo computer.
Quanto può essere grande il dump?
Il convertitore non tiene mai l'intero dump in memoria: legge il file due volte, per intervalli, e scrive lo script man mano. I dump di più gigabyte si convertono nel browser, nell'app desktop e con la riga di comando.
Posso convertire un file .dmp di exp o expdp?
Non direttamente: Data Pump e l'Export originale scrivono un formato binario che legge solo Oracle. Importalo in un qualsiasi database Oracle, Oracle Database Free gira in Docker, ed esportalo con Database Export di SQL Developer o con SQLcl. Il convertitore lo segnala quando riceve un file .dmp.
Da quali versioni di Oracle può provenire l'export?
Export di SQL Developer e SQLcl, con le definizioni di tabella di DBMS_METADATA, da Oracle 11g in poi. Ogni export di test viene da Oracle 21c e 23ai, in sessioni americane, tedesche e ISO, ed è confrontato con la sorgente cella per cella.
Cosa succede a NUMBER, DATE e agli altri tipi di Oracle?
NUMBER diventa numeric, o bigint quando ogni valore è un numero intero; DATE diventa timestamp, mantenendo l'ora; CLOB text, BLOB e RAW bytea, BINARY_DOUBLE double precision con NaN e Infinity, e gli intervalli interval.
Le colonne identity e le sequenze vengono mantenute?
Sì. Le colonne identity restano colonne identity e ripartono dopo l'id più alto, e così una colonna che un trigger riempie da una sequenza. Le sequenze ripartono da dove l'export dice che sono arrivate.
E package, procedure e trigger PL/SQL?
Sono elencati per nome nel report e non vengono convertiti, perché ogni database li scrive nel proprio linguaggio.
Su quali versioni di PostgreSQL si carica lo script?
PostgreSQL dalla 13 alla 18. Lo stesso script si carica su ognuna, fermandosi al primo errore se qualcosa va storto.