Skip to content
IntoSQL home

Move an Oracle export to an older or newer Oracle

Drop the .sql file SQL Developer's Database Export or SQLcl writes, with its tables and Insert statements, and download a script that loads on Oracle 19c and later. NUMBER, DATE, TIMESTAMP, LOBs, identities and sequences stay as they are, XMLTYPE and JSON keep their text in a CLOB, and every change is listed in the report.

Your file
sqlshop_2024_oracle.sqlSample file, 9.3 kB
Your database

18 rows converted, no fixes, none rejected

Show

Tables in shop_2024_oracle.sqlOracle dump · written by SQL Developer · utf-8

  • customerswritten as CUSTOMERS8 rows
    7 columns
    IDNUMBER(10,0) not null
    NAMEVARCHAR2(100 CHAR) not null
    EMAILVARCHAR2(150 CHAR) not null
    COUNTRYCHAR(2 BYTE) not null
    IS_ACTIVENUMBER(1,0) not null
    SIGNED_UPDATE
    CREATED_ATTIMESTAMP(0) not null
  • orderswritten as ORDERS10 rows
    9 columns
    IDNUMBER(10,0) not null
    CUSTOMER_IDNUMBER(10,0) not null
    INVOICENUMBER(10,0)
    STATUSVARCHAR2(10 CHAR) not null
    TOTALNUMBER(10,2) not null
    ITEMSNUMBER(5,0) not null
    ORDERED_ATDATE not null
    SHIPPED_ATDATE
    NOTEVARCHAR2(255 CHAR)
-- Written by IntoSQL from a Oracle dump. Load it with: NLS_LANG=.AL32UTF8 sqlplus -S -L <user>/<password>@<database> @<file>
WHENEVER SQLERROR EXIT FAILURE ROLLBACK;
SET DEFINE OFF;
SET SQLBLANKLINES ON;
SET FEEDBACK OFF;
SET SERVEROUTPUT ON;
CREATE OR REPLACE FUNCTION "INTOSQL_CLOB" (p SYS.ODCIVARCHAR2LIST) RETURN CLOB IS
    r CLOB;
BEGIN
    DBMS_LOB.CREATETEMPORARY(r, TRUE);
    FOR i IN 1 .. p.COUNT LOOP
        IF p(i) IS NOT NULL THEN
            DBMS_LOB.WRITEAPPEND(r, LENGTH2(p(i)), p(i));
        END IF;
    END LOOP;
    RETURN r;
END;
/
CREATE OR REPLACE FUNCTION "INTOSQL_BLOB" (p SYS.ODCIVARCHAR2LIST) RETURN BLOB IS
    r BLOB;
    piece RAW(2000);
BEGIN
    DBMS_LOB.CREATETEMPORARY(r, TRUE);
    FOR i IN 1 .. p.COUNT LOOP
        piece := HEXTORAW(p(i));
        DBMS_LOB.WRITEAPPEND(r, UTL_RAW.LENGTH(piece), piece);
    END LOOP;
    RETURN r;
END;
/

CREATE SEQUENCE "INVOICE_NUMBERS" START WITH 1020 INCREMENT BY 1 MINVALUE 1;
CREATE TABLE "CUSTOMERS" (
    "ID" NUMBER(10,0) GENERATED BY DEFAULT ON NULL AS IDENTITY (START WITH 1),
    "NAME" VARCHAR2(100 CHAR) NOT NULL,
    "EMAIL" VARCHAR2(150 CHAR) NOT NULL,
    "COUNTRY" CHAR(2 BYTE) DEFAULT 'US' NOT NULL,
    "IS_ACTIVE" NUMBER(1,0) DEFAULT 1 NOT NULL,
    "SIGNED_UP" DATE,
    "CREATED_AT" TIMESTAMP(0) DEFAULT LOCALTIMESTAMP NOT NULL,
    CONSTRAINT "CHECK1" CHECK ("IS_ACTIVE" IN (0, 1))
);

CREATE TABLE "ORDERS" (
    "ID" NUMBER(10,0) GENERATED BY DEFAULT ON NULL AS IDENTITY (START WITH 1),
    "CUSTOMER_ID" NUMBER(10,0) NOT NULL,
    "INVOICE" NUMBER(10,0) DEFAULT ("INVOICE_NUMBERS".NEXTVAL),
    "STATUS" VARCHAR2(10 CHAR) DEFAULT 'pending' NOT NULL,
    "TOTAL" NUMBER(10,2) NOT NULL,
    "ITEMS" NUMBER(5,0) DEFAULT 1 NOT NULL,
    "ORDERED_AT" DATE NOT NULL,
    "SHIPPED_AT" DATE,
    "NOTE" VARCHAR2(255 CHAR),
    CONSTRAINT "CHECK1_2" CHECK ("STATUS" IN ('pending', 'paid', 'shipped', 'cancelled'))
);

BEGIN
INSERT INTO "CUSTOMERS" (
"ID",
"NAME",
"EMAIL",
"COUNTRY",
"IS_ACTIVE",
"SIGNED_UP",
"CREATED_AT"
) VALUES (
1,
'Ana Martínez',
'ana@example.com',
'MX',
1,
TIMESTAMP '2023-01-15 00:00:00',
TIMESTAMP '2023-01-15 10:24:00.000000000'
);
COMMIT;
END;
/
BEGIN
INSERT INTO "CUSTOMERS" (
"ID",
"NAME",
"EMAIL",
"COUNTRY",
"IS_ACTIVE",
"SIGNED_UP",
"CREATED_AT"
) VALUES (
2,
'Zoë Müller',
'zoe@example.de',
'DE',
1,
TIMESTAMP '2023-02-03 00:00:00',
TIMESTAMP '2023-02-03 08:11:42.000000000'
);
COMMIT;
END;
/
BEGIN
INSERT INTO "CUSTOMERS" (
"ID",
"NAME",
"EMAIL",
"COUNTRY",
"IS_ACTIVE",
"SIGNED_UP",
"CREATED_AT"
) VALUES (
3,
'José Peña',
'jose@example.es',
'ES',
0,
TIMESTAMP '2023-03-21 00:00:00',
TIMESTAMP '2023-03-21 19:05:17.000000000'
);
COMMIT;
END;
/
BEGIN
INSERT INTO "CUSTOMERS" (
"ID",
"NAME",
"EMAIL",
"COUNTRY",
"IS_ACTIVE",
"SIGNED_UP",
"CREATED_AT"
) VALUES (
4,
'李雷',
'lilei@example.cn',
'CN',
1,
TIMESTAMP '2023-05-09 00:00:00',
TIMESTAMP '2023-05-09 02:40:00.000000000'
);
COMMIT;
END;
/
BEGIN
INSERT INTO "CUSTOMERS" (
"ID",
"NAME",
"EMAIL",
"COUNTRY",
"IS_ACTIVE",
"SIGNED_UP",
"CREATED_AT"
) VALUES (
5,
'Noah O''Brien',
'noah@example.ie',
'IE',
1,
NULL,
TIMESTAMP '2023-06-30 14:00:00.000000000'
);
COMMIT;
END;
/
BEGIN
INSERT INTO "CUSTOMERS" (
"ID",
"NAME",
"EMAIL",
"COUNTRY",
"IS_ACTIVE",
"SIGNED_UP",
"CREATED_AT"
) VALUES (
6,
'Amélie Laurent',
'amelie@example.fr',
'FR',
1,
TIMESTAMP '2024-01-02 00:00:00',
TIMESTAMP '2024-01-02 09:30:00.000000000'
);
COMMIT;
END;
/
BEGIN
INSERT INTO "CUSTOMERS" (
"ID",
"NAME",
"EMAIL",
"COUNTRY",
"IS_ACTIVE",
"SIGNED_UP",
"CREATED_AT"
) VALUES (
7,
'Ravi Patel',
'ravi@example.in',
'IN',
0,
TIMESTAMP '2024-02-29 00:00:00',
TIMESTAMP '2024-02-29 23:59:59.000000000'
);
COMMIT;
END;
/
BEGIN
INSERT INTO "CUSTOMERS" (
"ID",
"NAME",
"EMAIL",
"COUNTRY",
"IS_ACTIVE",
"SIGNED_UP",
"CREATED_AT"
) VALUES (
8,
'Sofia Rossi 🌻',
'sofia@example.it',
'IT',
1,
TIMESTAMP '2024-04-12 00:00:00',
TIMESTAMP '2024-04-12 12:12:12.000000000'
);
COMMIT;
END;
/
BEGIN
INSERT INTO "ORDERS" (
"ID",
"CUSTOMER_ID",
"INVOICE",
"STATUS",
"TOTAL",
"ITEMS",
"ORDERED_AT",
"SHIPPED_AT",
"NOTE"
) VALUES (
1,
1,
1000,
'shipped',
129.9,
3,
TIMESTAMP '2024-03-01 10:00:00',
TIMESTAMP '2024-03-02 16:30:00',
NULL
);
COMMIT;
END;
/
BEGIN
INSERT INTO "ORDERS" (
"ID",
"CUSTOMER_ID",
"INVOICE",
"STATUS",
"TOTAL",
"ITEMS",
"ORDERED_AT",
"SHIPPED_AT",
"NOTE"
) VALUES (
2,
2,
1001,
'paid',
54,
1,
TIMESTAMP '2024-03-02 11:15:00',
NULL,
'Gift wrap 🎁'
);
COMMIT;
END;
/
BEGIN
INSERT INTO "ORDERS" (
"ID",
"CUSTOMER_ID",
"INVOICE",
"STATUS",
"TOTAL",
"ITEMS",
"ORDERED_AT",
"SHIPPED_AT",
"NOTE"
) VALUES (
3,
1,
1002,
'cancelled',
12.5,
1,
TIMESTAMP '2024-03-03 09:45:00',
NULL,
'Customer changed their mind'
);
COMMIT;
END;
/
BEGIN
INSERT INTO "ORDERS" (
"ID",
"CUSTOMER_ID",
"INVOICE",
"STATUS",
"TOTAL",
"ITEMS",
"ORDERED_AT",
"SHIPPED_AT",
"NOTE"
) VALUES (
4,
4,
1003,
'shipped',
980,
12,
TIMESTAMP '2024-03-05 02:10:00',
TIMESTAMP '2024-03-07 08:00:00',
'请小心轻放'
);
COMMIT;
END;
/
BEGIN
INSERT INTO "ORDERS" (
"ID",
"CUSTOMER_ID",
"INVOICE",
"STATUS",
"TOTAL",
"ITEMS",
"ORDERED_AT",
"SHIPPED_AT",
"NOTE"
) VALUES (
5,
5,
1004,
'pending',
45.75,
2,
TIMESTAMP '2024-03-08 17:20:00',
NULL,
'Leave at the door; ring twice'
);
COMMIT;
END;
/
BEGIN
INSERT INTO "ORDERS" (
"ID",
"CUSTOMER_ID",
"INVOICE",
"STATUS",
"TOTAL",
"ITEMS",
"ORDERED_AT",
"SHIPPED_AT",
"NOTE"
) VALUES (
6,
6,
1005,
'shipped',
310.4,
5,
TIMESTAMP '2024-03-10 13:00:00',
TIMESTAMP '2024-03-11 09:00:00',
'Fragile: "glass"'
);
COMMIT;
END;
/
BEGIN
INSERT INTO "ORDERS" (
"ID",
"CUSTOMER_ID",
"INVOICE",
"STATUS",
"TOTAL",
"ITEMS",
"ORDERED_AT",
"SHIPPED_AT",
"NOTE"
) VALUES (
7,
8,
1006,
'paid',
76.2,
2,
TIMESTAMP '2024-03-11 18:45:00',
NULL,
NULL
);
COMMIT;
END;
/
BEGIN
INSERT INTO "ORDERS" (
"ID",
"CUSTOMER_ID",
"INVOICE",
"STATUS",
"TOTAL",
"ITEMS",
"ORDERED_AT",
"SHIPPED_AT",
"NOTE"
) VALUES (
8,
2,
1007,
'paid',
19.99,
1,
TIMESTAMP '2024-03-12 08:00:00',
NULL,
'Paid at the counter'
);
COMMIT;
END;
/
BEGIN
INSERT INTO "ORDERS" (
"ID",
"CUSTOMER_ID",
"INVOICE",
"STATUS",
"TOTAL",
"ITEMS",
"ORDERED_AT",
"SHIPPED_AT",
"NOTE"
) VALUES (
9,
3,
1008,
'shipped',
64,
2,
TIMESTAMP '2024-03-14 15:30:00',
NULL,
'Shipping date never recorded'
);
COMMIT;
END;
/
BEGIN
INSERT INTO "ORDERS" (
"ID",
"CUSTOMER_ID",
"INVOICE",
"STATUS",
"TOTAL",
"ITEMS",
"ORDERED_AT",
"SHIPPED_AT",
"NOTE"
) VALUES (
10,
7,
1009,
'paid',
1499,
1,
TIMESTAMP '2024-03-15 21:05:00',
NULL,
'C:\Users\ravi\invoice.pdf'
);
COMMIT;
END;
/

-- Keys, indexes, foreign keys, counters and triggers, after the rows.
ALTER TABLE "CUSTOMERS" ADD CONSTRAINT "PK_CUSTOMERS" PRIMARY KEY ("ID");
ALTER TABLE "CUSTOMERS" ADD CONSTRAINT "UQ_CUSTOMERS_CUSTOMERS_EMAIL_UK" UNIQUE ("EMAIL");
ALTER TABLE "ORDERS" ADD CONSTRAINT "PK_ORDERS" PRIMARY KEY ("ID");
CREATE INDEX "IX_ORDERS_ORDERS_STATUS_DATE_IX" ON "ORDERS" ("STATUS", "ORDERED_AT");
ALTER TABLE "ORDERS" ADD CONSTRAINT "ORDERS_CUSTOMER_FK" FOREIGN KEY ("CUSTOMER_ID") REFERENCES "CUSTOMERS" ("ID") ENABLE NOVALIDATE;
BEGIN
    EXECUTE IMMEDIATE 'ALTER TABLE "ORDERS" ENABLE VALIDATE CONSTRAINT "ORDERS_CUSTOMER_FK"';
EXCEPTION
    WHEN OTHERS THEN
        DBMS_OUTPUT.PUT_LINE('Foreign key ' || '"ORDERS_CUSTOMER_FK"' || ' has rows without a parent: it stays unvalidated, and new rows are checked.');
END;
/
DECLARE
    next_id NUMBER;
BEGIN
    SELECT NVL(MAX("ID"), 0) + 1 INTO next_id FROM "CUSTOMERS";
    IF next_id < 1 THEN
        next_id := 1;
    END IF;
    EXECUTE IMMEDIATE 'ALTER TABLE "CUSTOMERS" MODIFY ("ID" GENERATED BY DEFAULT ON NULL AS IDENTITY (START WITH ' || next_id || '))';
END;
/
COMMENT ON TABLE "CUSTOMERS" IS 'People who bought at least once';
DECLARE
    next_id NUMBER;
BEGIN
    SELECT NVL(MAX("ID"), 0) + 1 INTO next_id FROM "ORDERS";
    IF next_id < 1 THEN
        next_id := 1;
    END IF;
    EXECUTE IMMEDIATE 'ALTER TABLE "ORDERS" MODIFY ("ID" GENERATED BY DEFAULT ON NULL AS IDENTITY (START WITH ' || next_id || '))';
END;
/
DROP FUNCTION "INTOSQL_CLOB";
DROP FUNCTION "INTOSQL_BLOB";
COMMIT;
EXIT;

Fixes

Nothing needed fixing.

Your file is converted on this computer and never uploaded.

Other samples:

What changes between the file and the script

Each row is a rule the converter applies, with the report line it writes when the rule changes something in your data.

  • BOOLEAN (23ai)

    BOOLEAN on 23ai, else NUMBER(1) with a CHECK of 0 and 1

    TRUE and FALSE stay true and false.

  • VARCHAR2(n BYTE|CHAR), NVARCHAR2(n), CHAR(n), NCHAR(n), CLOB, NCLOB, LONG

    the same types, with their BYTE or CHAR lengths

    The same text types; LONG becomes CLOB, which every version since 8 prefers.

  • VARCHAR2 and NVARCHAR2 past what the target's own types hold

    VARCHAR2(4000 CHAR) when every value fits 4000 bytes, else CLOB

    A VARCHAR2 holds 4000 bytes by default and Oracle can't index a CLOB, so text keeps VARCHAR2 when its values fit and is a CLOB when they don't.

    Report line: text_as_lob

  • NUMBER, NUMBER(p,s), FLOAT(b)

    the same NUMBER types

    The same NUMBER and FLOAT types, with every digit.

  • INTERVAL YEAR TO MONTH, INTERVAL DAY TO SECOND

    the same INTERVAL types

    The same intervals.

  • DATE, TIMESTAMP(p)

    DATE and TIMESTAMP(p), as declared

    The same types, so every date keeps its time to the second.

  • TIMESTAMP WITH TIME ZONE, TIMESTAMP WITH LOCAL TIME ZONE

    the same types

    Oracle keeps the offsets from -12:00 to +14:00; a value with an offset beyond -12:00 is written in UTC, the same instant.

  • JSON (21c), and CLOB holding JSON

    JSON on 21c and later, else CLOB

    The same JSON values; a SQL NULL the export wrote as 'null' stays NULL.

  • BLOB, RAW(n), LONG RAW

    the same types; LONG RAW as BLOB

    Bytes stay bytes.

  • ROWID, UROWID, XMLTYPE, SDO_GEOMETRY

    CLOB with their text

    XMLTYPE, ROWID and UROWID keep their text in a CLOB, so the script loads on every version from 19c.

    Report line: type_replaced

  • collations that ignore case (BINARY_CI)

    exact comparison

    Column collations aren't written, so a column that ignored case (BINARY_CI) now tells Ana and ana apart.

    Report line: case_insensitive_collation

  • identity columns, and a sequence a BEFORE INSERT trigger assigns

    GENERATED BY DEFAULT ON NULL AS IDENTITY, continuing after the highest id

    The copied ids load as they are, and the counter continues after the highest one. A column a trigger fills from a sequence (the way before 12c) is an identity too.

  • identity columns the target can't count

    a plain integer

    Oracle counts one identity column per table; any other keeps its values without a counter.

    Report line: counter_dropped

  • CREATE SEQUENCE

    CREATE SEQUENCE with the same options

    The export writes where a sequence has reached as its START WITH, so it continues from there.

  • virtual columns (GENERATED ALWAYS AS ... VIRTUAL)

    virtual columns

    The same virtual columns.

    Report line: stored_as_virtual

  • virtual columns with functions IntoSQL doesn't write, and the || operator

    a plain column

    A formula that can't be written for Oracle leaves a plain column and a report line; the values the export wrote for virtual columns are left out, and Oracle has none to compute.

    Report line: generated_not_translated

  • defaults with functions IntoSQL doesn't write (SYS_GUID(), ||)

    no default

    A default that can't be written is left out and reported.

    Report line: default_not_translated

  • CHECK constraints

    translated

    The same checks, tested as each row loads.

  • CHECK constraints with functions IntoSQL doesn't write

    not created

    A check that can't be written is left out and reported.

    Report line: check_skipped

  • CHECK constraints that are DISABLE or ENABLE NOVALIDATE

    not created

    Oracle doesn't apply these checks to the rows already there, so the rows may break them.

    Report line: check_not_enforced

  • function-based, bitmap and domain indexes

    not created

    IntoSQL writes b-tree indexes on whole columns; create the others again after loading.

    Report line: index_skipped

  • foreign keys

    after the data ENABLE NOVALIDATE, then validated with a warning on orphans

    The key checks new rows at once and is validated last; rows without a parent leave it unvalidated with a warning instead of stopping the load.

  • ON DELETE CASCADE that cascades in a cycle or along a second path

    dropped

    Oracle's foreign keys act on delete only, with CASCADE or SET NULL.

    Report line: foreign_key_action_unsupported

  • foreign keys to tables the export doesn't create, and DISABLE or NOVALIDATE ones

    not created

    A key can't name a table that isn't there, and one Oracle doesn't check may have rows without a parent.

    Report line: foreign_key_skipped

  • names longer than Oracle 12.1 allows

    shortened

    Oracle names hold 128 bytes from 12.2; a longer name is shortened.

    Report line: name_shortened

  • partitioned tables

    one table with every partition's rows

    The rows are the same; partition the table again in Oracle if it needs it.

    Report line: partitions_dropped

  • several schemas in one export

    one schema, tables named after their schema first

    The script loads into the schema of the user who runs it, so tables of different schemas get their schema's name in front; an export of one schema keeps its tables' names.

    Report line: schemas_merged

  • NOT NULL columns with values that become NULL

    NOT NULL dropped

    A column whose values had to become NULL can't keep NOT NULL, or the load would stop.

    Report line: not_null_relaxed

  • views, synonyms, types, packages, procedures, functions and other triggers

    listed in the report

    Their code is written in PL/SQL, so they are listed for you to recreate.

    Report line: not_converted_objects

  • exports without CREATE TABLE (data only)

    types read from the values

    A data-only export has only rows, so the types come from the values.

    Report line: no_schema

  • expressions in INSERT values other than the ones exports write

    NULL

    A converter that doesn't run a database can't evaluate them; to_date, to_timestamp, TO_CLOB, HEXTORAW and the others exports write are read.

    Report line: expression_value

  • types IntoSQL doesn't know, and object types

    CLOB

    Text holds any value; change the type after loading.

    Report line: unknown_type

  • dates before year 1 and time zones given as region names

    NULL

    A value that isn't a date becomes NULL and the report counts it.

    Report line: date_out_of_range

  • NaN and Infinity in BINARY_FLOAT and BINARY_DOUBLE

    NaN and Infinity kept

    Oracle's floating-point types hold NaN and infinity.

  • NUL characters in text

    kept

    VARCHAR2 and CLOB can hold a NUL character, so the value is copied as it is.

  • text that isn't valid in its encoding

    the bad bytes as the replacement character

    The script is UTF-8 and every value has to be valid text.

    Report line: invalid_utf8_replaced

  • text decoded twice (ñ)

    copied as it is

    The script holds exactly these characters, so they are copied; the report points them out.

    Report line: mojibake_suspected

In the fileIn the scriptReport line
BOOLEAN (23ai)BOOLEAN on 23ai, else NUMBER(1) with a CHECK of 0 and 1TRUE and FALSE stay true and false.
VARCHAR2(n BYTE|CHAR), NVARCHAR2(n), CHAR(n), NCHAR(n), CLOB, NCLOB, LONGthe same types, with their BYTE or CHAR lengthsThe same text types; LONG becomes CLOB, which every version since 8 prefers.
VARCHAR2 and NVARCHAR2 past what the target's own types holdVARCHAR2(4000 CHAR) when every value fits 4000 bytes, else CLOBA VARCHAR2 holds 4000 bytes by default and Oracle can't index a CLOB, so text keeps VARCHAR2 when its values fit and is a CLOB when they don't.text_as_lob
NUMBER, NUMBER(p,s), FLOAT(b)the same NUMBER typesThe same NUMBER and FLOAT types, with every digit.
INTERVAL YEAR TO MONTH, INTERVAL DAY TO SECONDthe same INTERVAL typesThe same intervals.
DATE, TIMESTAMP(p)DATE and TIMESTAMP(p), as declaredThe same types, so every date keeps its time to the second.
TIMESTAMP WITH TIME ZONE, TIMESTAMP WITH LOCAL TIME ZONEthe same typesOracle keeps the offsets from -12:00 to +14:00; a value with an offset beyond -12:00 is written in UTC, the same instant.
JSON (21c), and CLOB holding JSONJSON on 21c and later, else CLOBThe same JSON values; a SQL NULL the export wrote as 'null' stays NULL.
BLOB, RAW(n), LONG RAWthe same types; LONG RAW as BLOBBytes stay bytes.
ROWID, UROWID, XMLTYPE, SDO_GEOMETRYCLOB with their textXMLTYPE, ROWID and UROWID keep their text in a CLOB, so the script loads on every version from 19c.type_replaced
collations that ignore case (BINARY_CI)exact comparisonColumn collations aren't written, so a column that ignored case (BINARY_CI) now tells Ana and ana apart.case_insensitive_collation
identity columns, and a sequence a BEFORE INSERT trigger assignsGENERATED BY DEFAULT ON NULL AS IDENTITY, continuing after the highest idThe copied ids load as they are, and the counter continues after the highest one. A column a trigger fills from a sequence (the way before 12c) is an identity too.
identity columns the target can't counta plain integerOracle counts one identity column per table; any other keeps its values without a counter.counter_dropped
CREATE SEQUENCECREATE SEQUENCE with the same optionsThe export writes where a sequence has reached as its START WITH, so it continues from there.
virtual columns (GENERATED ALWAYS AS ... VIRTUAL)virtual columnsThe same virtual columns.stored_as_virtual
virtual columns with functions IntoSQL doesn't write, and the || operatora plain columnA formula that can't be written for Oracle leaves a plain column and a report line; the values the export wrote for virtual columns are left out, and Oracle has none to compute.generated_not_translated
defaults with functions IntoSQL doesn't write (SYS_GUID(), ||)no defaultA default that can't be written is left out and reported.default_not_translated
CHECK constraintstranslatedThe same checks, tested as each row loads.
CHECK constraints with functions IntoSQL doesn't writenot createdA check that can't be written is left out and reported.check_skipped
CHECK constraints that are DISABLE or ENABLE NOVALIDATEnot createdOracle doesn't apply these checks to the rows already there, so the rows may break them.check_not_enforced
function-based, bitmap and domain indexesnot createdIntoSQL writes b-tree indexes on whole columns; create the others again after loading.index_skipped
foreign keysafter the data ENABLE NOVALIDATE, then validated with a warning on orphansThe key checks new rows at once and is validated last; rows without a parent leave it unvalidated with a warning instead of stopping the load.
ON DELETE CASCADE that cascades in a cycle or along a second pathdroppedOracle's foreign keys act on delete only, with CASCADE or SET NULL.foreign_key_action_unsupported
foreign keys to tables the export doesn't create, and DISABLE or NOVALIDATE onesnot createdA key can't name a table that isn't there, and one Oracle doesn't check may have rows without a parent.foreign_key_skipped
names longer than Oracle 12.1 allowsshortenedOracle names hold 128 bytes from 12.2; a longer name is shortened.name_shortened
partitioned tablesone table with every partition's rowsThe rows are the same; partition the table again in Oracle if it needs it.partitions_dropped
several schemas in one exportone schema, tables named after their schema firstThe script loads into the schema of the user who runs it, so tables of different schemas get their schema's name in front; an export of one schema keeps its tables' names.schemas_merged
NOT NULL columns with values that become NULLNOT NULL droppedA column whose values had to become NULL can't keep NOT NULL, or the load would stop.not_null_relaxed
views, synonyms, types, packages, procedures, functions and other triggerslisted in the reportTheir code is written in PL/SQL, so they are listed for you to recreate.not_converted_objects
exports without CREATE TABLE (data only)types read from the valuesA data-only export has only rows, so the types come from the values.no_schema
expressions in INSERT values other than the ones exports writeNULLA converter that doesn't run a database can't evaluate them; to_date, to_timestamp, TO_CLOB, HEXTORAW and the others exports write are read.expression_value
types IntoSQL doesn't know, and object typesCLOBText holds any value; change the type after loading.unknown_type
dates before year 1 and time zones given as region namesNULLA value that isn't a date becomes NULL and the report counts it.date_out_of_range
NaN and Infinity in BINARY_FLOAT and BINARY_DOUBLENaN and Infinity keptOracle's floating-point types hold NaN and infinity.
NUL characters in textkeptVARCHAR2 and CLOB can hold a NUL character, so the value is copied as it is.
text that isn't valid in its encodingthe bad bytes as the replacement characterThe script is UTF-8 and every value has to be valid text.invalid_utf8_replaced
text decoded twice (ñ)copied as it isThe script holds exactly these characters, so they are copied; the report points them out.mojibake_suspected

Load the script into Oracle

The script creates the tables, copies the rows, then adds keys, indexes and foreign keys, so it loads fast and stops at the first error.

NLS_LANG=.AL32UTF8 sqlplus -S -L user@//localhost/mydb @oracle.oracle.sql

Foreign keys are added ENABLE NOVALIDATE, so new rows are checked at once, and then validated. If an old row points to a row that doesn't exist, the script says which constraint and leaves it unvalidated, instead of failing half way.

For a large dump, run SQL*Plus on the same machine as the database. The script commits every 100 rows. The user that runs it needs CREATE TABLE, CREATE SEQUENCE, CREATE TRIGGER and CREATE PROCEDURE, and quota on its tablespace.

Dump Oracle for the best result

The converter reads what the dump holds. These options keep more of your data in it:

  • In SQL Developer, choose Tools, Database Export, with DDL and the Insert data format. SQLcl writes the same with SET SQLFORMAT INSERT.
  • Run ALTER SESSION SET NLS_DATE_FORMAT = 'SYYYY-MM-DD HH24:MI:SS' before exporting: the default format writes dates without their time or century.
  • A .dmp file from exp or expdp is binary: import it into an Oracle database, Oracle Database Free in Docker for one, and export it with SQL Developer.

What isn't converted

Views, triggers, procedures, functions and other objects written in Oracle's own language are listed in the report by name, never dropped silently, so you can check each one against the Oracle version you load into before recreating it.

Questions about moving Oracle between versions

Not answered here? Write to us from the contact page and a person replies. Contact page

Is my dump uploaded?

No. The converter runs in your browser and reads the dump from your disk in pieces, so it never leaves your computer. The desktop app and the command line convert on your computer too.

How large can the dump be?

The converter never holds the whole dump in memory: it reads the file twice, by ranges, and writes the script as it goes. Multi-gigabyte dumps convert in the browser, in the desktop app and with the command line.

Can I convert a .dmp file from exp or expdp?

Not directly: Data Pump and the original Export write a binary format only Oracle reads. Import it into any Oracle database, Oracle Database Free runs in Docker, and export it with SQL Developer's Database Export or SQLcl. The converter says so when it gets a .dmp file.

Which Oracle versions can the export come from?

Exports from SQL Developer and SQLcl, with DBMS_METADATA's table definitions, from Oracle 11g on. Every test export comes from Oracle 21c and 23ai, in American, German and ISO sessions, and is compared with its source cell by cell.

What happens to NUMBER, DATE and the other Oracle types?

They stay as they are: NUMBER with its precision, DATE, TIMESTAMP, intervals, CHAR and VARCHAR2 with their lengths, RAW and the LOBs. XMLTYPE and JSON keep their text in a CLOB, so the script loads on 19c.

Are identity columns and sequences kept?

Yes. Identity columns and sequences stay as they are and continue from where the export says they reached.

What about PL/SQL packages, procedures and triggers?

They are listed in the report by name and not converted, so you can recreate them once the tables are in.

Which Oracle versions does the script load on?

Oracle 19c and later. Every test export loads on 21c and 23ai, stopping at the first error if anything goes wrong.