Перейти к содержимому
Главная страница IntoSQL

Конвертация PostgreSQL в Oracle

Перетащите обычный дамп из pg_dump или pg_dumpall в формате .sql или .sql.gz и скачайте скрипт Oracle с теми же таблицами, ключами, индексами и данными. Каждое значение, которое Oracle не может хранить как есть, и каждый объект, который он не переносит, указаны в отчете.

Ваш файл
sqlshop_2024_postgres.sqlПример файла, 6,2 кБ
Ваша база данных

18 строк конвертировано, 2 исправления, ни одна не отклонена

Показать

Таблицы в shop_2024_postgres.sqlДамп PostgreSQL 18.6 · создан программой pg_dump · utf8mb4

  • customersзаписана как CUSTOMERS8 строк1
    8 столбцов
    IDNUMBER(10) not null
    NAMEVARCHAR2(4000 CHAR) not null
    EMAILVARCHAR2(750 CHAR) not null
    COUNTRYVARCHAR2(2 CHAR) not null
    IS_ACTIVENUMBER(1) not null
    TAGSCLOB not null
    SIGNED_UPDATE
    CREATED_ATTIMESTAMP(6) WITH TIME ZONE not null
  • ordersзаписана как ORDERS10 строк1
    10 столбцов
    IDNUMBER(10) not null
    CUSTOMER_IDNUMBER(10) not null
    STATUSVARCHAR2(9 CHAR) not null
    TOTALNUMBER(10,2) not null
    ITEMSNUMBER(5) not null
    ORDERED_ATTIMESTAMP(6) not null
    SHIPPED_ATTIMESTAMP(6)
    NOTEVARCHAR2(4000 CHAR)
    DETAILSCLOB
    DELIVERYVARCHAR2(100 CHAR)

Не конвертировано

  • Представление paid_orders

Каждая база данных пишет их на своем языке. Перепишите их для Oracle.

-- Written by IntoSQL from a PostgreSQL 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 TABLE "CUSTOMERS" (
    "ID" NUMBER(10) GENERATED BY DEFAULT ON NULL AS IDENTITY (START WITH 1001),
    "NAME" VARCHAR2(4000 CHAR) NOT NULL,
    "EMAIL" VARCHAR2(750 CHAR) NOT NULL,
    "COUNTRY" VARCHAR2(2 CHAR) DEFAULT 'US' NOT NULL,
    "IS_ACTIVE" NUMBER(1) DEFAULT 1 NOT NULL,
    "TAGS" CLOB NOT NULL,
    "SIGNED_UP" DATE,
    "CREATED_AT" TIMESTAMP(6) WITH TIME ZONE DEFAULT SYSTIMESTAMP NOT NULL,
    CONSTRAINT "CK_CUSTOMERS_IS_ACTIVE" CHECK ("IS_ACTIVE" IN (0, 1))
);

CREATE TABLE "ORDERS" (
    "ID" NUMBER(10) GENERATED BY DEFAULT ON NULL AS IDENTITY (START WITH 5001),
    "CUSTOMER_ID" NUMBER(10) NOT NULL,
    "STATUS" VARCHAR2(9 CHAR) DEFAULT 'pending' NOT NULL,
    "TOTAL" NUMBER(10,2) NOT NULL,
    "ITEMS" NUMBER(5) DEFAULT 1 NOT NULL,
    "ORDERED_AT" TIMESTAMP(6) NOT NULL,
    "SHIPPED_AT" TIMESTAMP(6),
    "NOTE" VARCHAR2(4000 CHAR),
    "DETAILS" CLOB,
    "DELIVERY" VARCHAR2(100 CHAR),
    CONSTRAINT "CK_ORDERS_STATUS" CHECK ("STATUS" IN ('pending', 'paid', 'shipped', 'cancelled')),
    CONSTRAINT "ORDERS_TOTAL_CHECK" CHECK ("TOTAL" >= (0))
);

BEGIN
INSERT INTO "CUSTOMERS" (
"ID",
"NAME",
"EMAIL",
"COUNTRY",
"IS_ACTIVE",
"TAGS",
"SIGNED_UP",
"CREATED_AT"
) VALUES (
1,
'Ana Martínez',
'ana@example.com',
'MX',
1,
'["vip"]',
DATE '2023-01-15',
TIMESTAMP '2023-01-15 10:24:00 +00:00'
);
INSERT INTO "CUSTOMERS" (
"ID",
"NAME",
"EMAIL",
"COUNTRY",
"IS_ACTIVE",
"TAGS",
"SIGNED_UP",
"CREATED_AT"
) VALUES (
2,
'Zoë Müller',
'zoe@example.de',
'DE',
1,
'[]',
DATE '2023-02-03',
TIMESTAMP '2023-02-03 08:11:42 +00:00'
);
INSERT INTO "CUSTOMERS" (
"ID",
"NAME",
"EMAIL",
"COUNTRY",
"IS_ACTIVE",
"TAGS",
"SIGNED_UP",
"CREATED_AT"
) VALUES (
3,
'José Peña',
'jose@example.es',
'ES',
0,
'["wholesale","late payer"]',
DATE '2023-03-21',
TIMESTAMP '2023-03-21 19:05:17 +00:00'
);
INSERT INTO "CUSTOMERS" (
"ID",
"NAME",
"EMAIL",
"COUNTRY",
"IS_ACTIVE",
"TAGS",
"SIGNED_UP",
"CREATED_AT"
) VALUES (
4,
'李雷',
'lilei@example.cn',
'CN',
1,
'[]',
DATE '2023-05-09',
TIMESTAMP '2023-05-09 02:40:00 +00:00'
);
INSERT INTO "CUSTOMERS" (
"ID",
"NAME",
"EMAIL",
"COUNTRY",
"IS_ACTIVE",
"TAGS",
"SIGNED_UP",
"CREATED_AT"
) VALUES (
5,
'Noah O''Brien',
'noah@example.ie',
'IE',
1,
'[]',
NULL,
TIMESTAMP '2023-06-30 14:00:00 +00:00'
);
INSERT INTO "CUSTOMERS" (
"ID",
"NAME",
"EMAIL",
"COUNTRY",
"IS_ACTIVE",
"TAGS",
"SIGNED_UP",
"CREATED_AT"
) VALUES (
6,
'Amélie Laurent',
'amelie@example.fr',
'FR',
1,
'["vip"]',
DATE '2024-01-02',
TIMESTAMP '2024-01-02 09:30:00 +00:00'
);
INSERT INTO "CUSTOMERS" (
"ID",
"NAME",
"EMAIL",
"COUNTRY",
"IS_ACTIVE",
"TAGS",
"SIGNED_UP",
"CREATED_AT"
) VALUES (
7,
'Ravi Patel',
'ravi@example.in',
'IN',
0,
'[]',
DATE '2024-02-29',
TIMESTAMP '2024-02-29 23:59:59 +00:00'
);
INSERT INTO "CUSTOMERS" (
"ID",
"NAME",
"EMAIL",
"COUNTRY",
"IS_ACTIVE",
"TAGS",
"SIGNED_UP",
"CREATED_AT"
) VALUES (
8,
'Sofia Rossi 🌻',
'sofia@example.it',
'IT',
1,
'[]',
DATE '2024-04-12',
TIMESTAMP '2024-04-12 12:12:12 +00:00'
);
COMMIT;
END;
/
BEGIN
INSERT INTO "ORDERS" (
"ID",
"CUSTOMER_ID",
"STATUS",
"TOTAL",
"ITEMS",
"ORDERED_AT",
"SHIPPED_AT",
"NOTE",
"DETAILS",
"DELIVERY"
) VALUES (
1,
1,
'shipped',
129.90,
3,
TIMESTAMP '2024-03-01 10:00:00',
TIMESTAMP '2024-03-02 16:30:00',
NULL,
'{"gift": false}',
'1 day 06:30:00'
);
INSERT INTO "ORDERS" (
"ID",
"CUSTOMER_ID",
"STATUS",
"TOTAL",
"ITEMS",
"ORDERED_AT",
"SHIPPED_AT",
"NOTE",
"DETAILS",
"DELIVERY"
) VALUES (
2,
2,
'paid',
54.00,
1,
TIMESTAMP '2024-03-02 11:15:00',
NULL,
'Gift wrap 🎁',
'{"gift": true, "wrap": "red"}',
NULL
);
INSERT INTO "ORDERS" (
"ID",
"CUSTOMER_ID",
"STATUS",
"TOTAL",
"ITEMS",
"ORDERED_AT",
"SHIPPED_AT",
"NOTE",
"DETAILS",
"DELIVERY"
) VALUES (
3,
1,
'cancelled',
12.50,
1,
TIMESTAMP '2024-03-03 09:45:00',
NULL,
'Customer changed their mind',
NULL,
NULL
);
INSERT INTO "ORDERS" (
"ID",
"CUSTOMER_ID",
"STATUS",
"TOTAL",
"ITEMS",
"ORDERED_AT",
"SHIPPED_AT",
"NOTE",
"DETAILS",
"DELIVERY"
) VALUES (
4,
4,
'shipped',
980.00,
12,
TIMESTAMP '2024-03-05 02:10:00',
TIMESTAMP '2024-03-07 08:00:00',
'请小心轻放',
'{"fragile": true}',
'2 days 05:50:00'
);
INSERT INTO "ORDERS" (
"ID",
"CUSTOMER_ID",
"STATUS",
"TOTAL",
"ITEMS",
"ORDERED_AT",
"SHIPPED_AT",
"NOTE",
"DETAILS",
"DELIVERY"
) VALUES (
5,
5,
'pending',
45.75,
2,
TIMESTAMP '2024-03-08 17:20:00',
NULL,
'Leave at the door; ring twice',
NULL,
NULL
);
INSERT INTO "ORDERS" (
"ID",
"CUSTOMER_ID",
"STATUS",
"TOTAL",
"ITEMS",
"ORDERED_AT",
"SHIPPED_AT",
"NOTE",
"DETAILS",
"DELIVERY"
) VALUES (
6,
6,
'shipped',
310.40,
5,
TIMESTAMP '2024-03-10 13:00:00',
TIMESTAMP '2024-03-11 09:00:00',
'Fragile: "glass"',
'{"fragile": true}',
'20:00:00'
);
INSERT INTO "ORDERS" (
"ID",
"CUSTOMER_ID",
"STATUS",
"TOTAL",
"ITEMS",
"ORDERED_AT",
"SHIPPED_AT",
"NOTE",
"DETAILS",
"DELIVERY"
) VALUES (
7,
8,
'paid',
76.20,
2,
TIMESTAMP '2024-03-11 18:45:00',
NULL,
NULL,
NULL,
NULL
);
INSERT INTO "ORDERS" (
"ID",
"CUSTOMER_ID",
"STATUS",
"TOTAL",
"ITEMS",
"ORDERED_AT",
"SHIPPED_AT",
"NOTE",
"DETAILS",
"DELIVERY"
) VALUES (
8,
2,
'shipped',
19.99,
1,
TIMESTAMP '2024-03-12 08:00:00',
NULL,
'Shipped date set to infinity by an old import',
NULL,
NULL
);
INSERT INTO "ORDERS" (
"ID",
"CUSTOMER_ID",
"STATUS",
"TOTAL",
"ITEMS",
"ORDERED_AT",
"SHIPPED_AT",
"NOTE",
"DETAILS",
"DELIVERY"
) VALUES (
9,
3,
'shipped',
64.00,
2,
TIMESTAMP '2024-03-14 15:30:00',
TIMESTAMP '2024-03-15 10:00:00',
'Two parcels',
'{"parcels": [1, 2]}',
'18:30:00'
);
INSERT INTO "ORDERS" (
"ID",
"CUSTOMER_ID",
"STATUS",
"TOTAL",
"ITEMS",
"ORDERED_AT",
"SHIPPED_AT",
"NOTE",
"DETAILS",
"DELIVERY"
) VALUES (
10,
7,
'paid',
1499.00,
1,
TIMESTAMP '2024-03-15 21:05:00',
NULL,
'C:\Users\ravi\invoice.pdf',
NULL,
NULL
);
COMMIT;
END;
/

-- Keys, indexes, foreign keys, counters and triggers, after the rows.
ALTER TABLE "CUSTOMERS" ADD CONSTRAINT "UQ_CUSTOMERS_CUSTOMERS_EMAIL_KEY" UNIQUE ("EMAIL");
ALTER TABLE "CUSTOMERS" ADD CONSTRAINT "PK_CUSTOMERS" PRIMARY KEY ("ID");
ALTER TABLE "ORDERS" ADD CONSTRAINT "PK_ORDERS" PRIMARY KEY ("ID");
CREATE INDEX "IX_ORDERS_IDX_ORDERS_CUSTOMER" ON "ORDERS" ("CUSTOMER_ID");
CREATE INDEX "IX_ORDERS_IDX_ORDERS_STATUS_DATE" ON "ORDERS" ("STATUS", "ORDERED_AT");
ALTER TABLE "ORDERS" ADD CONSTRAINT "ORDERS_CUSTOMER_ID_FKEY" FOREIGN KEY ("CUSTOMER_ID") REFERENCES "CUSTOMERS" ("ID") ENABLE NOVALIDATE;
BEGIN
    EXECUTE IMMEDIATE 'ALTER TABLE "ORDERS" ENABLE VALIDATE CONSTRAINT "ORDERS_CUSTOMER_ID_FKEY"';
EXCEPTION
    WHEN OTHERS THEN
        DBMS_OUTPUT.PUT_LINE('Foreign key ' || '"ORDERS_CUSTOMER_ID_FKEY"' || ' 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 < 1001 THEN
        next_id := 1001;
    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 < 5001 THEN
        next_id := 5001;
    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;

Исправления

  1. "tags" таблицы customers имеет тип text[], которого нет в Oracle, поэтому тип CLOB с теми же значениями.
  2. "delivery" таблицы orders имеет тип interval, которого нет в Oracle, поэтому тип VARCHAR2(100 CHAR) с теми же значениями.

Требует проверки

  • Не конвертированы представления, триггеры, процедуры и события (1 объект); отчет перечисляет каждый. Создайте их заново вручную в Oracle.
  • Значение по умолчанию столбца "tags" таблицы customers нельзя записать для Oracle (a value its type can't hold), поэтому у столбца его нет. Добавьте значение по умолчанию вручную, если оно нужно новым строкам.
  • Индекс idx_orders_details таблицы orders использует выражение, которое нельзя записать для Oracle (a gin index), поэтому он не создан. Создайте индекс вручную, если он нужен запросам.
  • В "shipped_at" таблицы orders есть даты, которые Oracle не может хранить (1 значение), например infinity; они заменены на NULL. Oracle хранит годы с 1 по 9999 без бесконечности; если эти даты важны, замените их в источнике.

Файл конвертируется на этом компьютере и никуда не загружается.

Другие примеры:

Что меняется при переходе с PostgreSQL на Oracle

Каждая строка - это правило, которое применяет конвертер, и строка отчета, которую он пишет, когда правило что-то меняет в ваших данных.

  • boolean

    NUMBER(1) с CHECK на 0 и 1

    В Oracle 21c нет логического типа столбца; 1 и 0 сохраняют true и false во всех версиях.

  • varchar(n), char(n)

    VARCHAR2(n CHAR), длина в символах

    Длины считаются в символах, как в PostgreSQL.

  • text

    VARCHAR2(4000 CHAR), если каждое значение укладывается в 4000 байт (750 символов в ключе), иначе CLOB

    VARCHAR2 по умолчанию вмещает 4000 байт, а Oracle не может индексировать CLOB, поэтому text остается VARCHAR2, если значения помещаются, и становится CLOB, если нет.

    Строка отчета: text_as_lob

  • empty strings and empty bytea values

    NULL

    Oracle хранит пустую строку как NULL, поэтому значение ' читается обратно как NULL. Отчет подсчитывает их по столбцам.

    Строка отчета: empty_string_null

  • citext

    точное сравнение

    Oracle сравнивает текст точно, если не указано иное, как и text в PostgreSQL; столбцы citext перестают игнорировать регистр.

    Строка отчета: case_insensitive_collation

  • numeric(p,s) past 38 digits; numeric without a precision

    NUMBER, если значения укладываются в 38 цифр, иначе VARCHAR2

    NUMBER в Oracle вмещает 38 цифр при любом масштабе. Более широкие числа хранятся как текст, чтобы каждая цифра осталась точной.

    Строка отчета: decimal_as_text

  • money

    NUMBER(19,2)

    В Oracle нет типа money. Сумма хранится как число, без символа валюты, добавленного локалью сервера.

    Строка отчета: type_replaced

  • interval

    VARCHAR2(100 CHAR) с текстом PostgreSQL

    interval смешивает месяцы с днями и секундами, которые Oracle хранит в двух разных типах; текст сохраняет каждую часть.

    Строка отчета: type_replaced

  • arrays (integer[], text[]...)

    CLOB с массивом JSON

    В Oracle нет массивов. Массив JSON сохраняет каждый элемент и NULL, а JSON_TABLE его читает.

    Строка отчета: type_replaced

  • ranges, geometric types, tsvector, xml, hstore, PostGIS, composite and extension types

    VARCHAR2 или CLOB с текстом PostgreSQL

    В Oracle нет равнозначного типа, поэтому каждое значение сохраняет текст, записанный PostgreSQL.

    Строка отчета: type_replaced

  • json, jsonb

    CLOB

    Функции JSON в Oracle читают JSON из текста во всех версиях, а CLOB вмещает значения любого размера.

  • timestamp

    TIMESTAMP

    timestamp without time zone - это показание часов, как TIMESTAMP в Oracle. date становится DATE, который хранит и время суток, оно остается полночью.

  • timestamp with time zone

    TIMESTAMP WITH TIME ZONE в UTC

    Каждое значение записывается в UTC: тот же момент, какое бы смещение ни записал дамп.

  • time

    INTERVAL DAY TO SECOND от полуночи

    В Oracle нет типа для времени суток; интервал от полуночи вмещает любое значение, включая 24:00:00.

  • infinity, -infinity, BC dates and years past 9999

    NULL

    Скрипт пишет годы с 1 по 9999, а в Oracle нет бесконечности. Такие значения становятся NULL, а отчет их подсчитывает.

    Строка отчета: date_out_of_range

  • NaN and Infinity in numeric

    NULL

    NUMBER в Oracle не поддерживает NaN и Infinity, поэтому такие значения становятся NULL, а отчет их подсчитывает.

    Строка отчета: invalid_value_null

  • NOT NULL columns with values that become NULL

    NOT NULL снимается

    Столбец, значения которого пришлось заменить на NULL, не может сохранить NOT NULL, иначе загрузка остановится.

    Строка отчета: not_null_relaxed

  • enum types

    VARCHAR2 с CHECK его меток

    Текст с проверкой хранит те же значения.

  • enum columns holding values outside their labels

    VARCHAR2 без проверки

    Значения, которых нет среди меток, не прошли бы проверку, поэтому она не создается.

    Строка отчета: enum_check_skipped

  • uuid, inet, cidr, macaddr

    VARCHAR2

    В Oracle нет таких типов; текст сохраняет каждое значение.

  • bytea

    BLOB

    Байты остаются байтами.

  • bit(n), bit varying

    NUMBER со значением битов до 63 бит; дальше цифры в тексте

    Битовые строки хранятся как число, которое составляют их биты.

  • oid, xid, cid

    NUMBER(10) с CHECK на неотрицательность

    NUMBER вмещает любое значение.

  • serial, identity

    GENERATED BY DEFAULT ON NULL AS IDENTITY, продолжается после наибольшего id

    Скопированные id загружаются как есть, а счетчик продолжается после наибольшего из них или со следующего значения последовательности, смотря что больше.

  • a second serial or identity column in a table

    обычное целое

    Oracle нумерует один столбец identity на таблицу; любой другой сохраняет свои значения без счетчика.

    Строка отчета: counter_dropped

  • sequences not owned by a column

    CREATE SEQUENCE с достигнутым значением

    Последовательности в Oracle работают так же.

  • generated columns

    виртуальные столбцы

    Oracle вычисляет генерируемые столбцы только при чтении; столбцы STORED становятся виртуальными с теми же значениями.

    Строка отчета: stored_as_virtual

  • generated columns with functions Oracle lacks

    обычный столбец со значениями

    Формула, которую нельзя записать для Oracle, оставляет обычный столбец со значениями из дампа и строку в отчете.

    Строка отчета: generated_not_translated

  • defaults with functions Oracle lacks, and defaults from a sequence

    без значения по умолчанию

    Значение по умолчанию, которое нельзя записать для Oracle, опускается и попадает в отчет.

    Строка отчета: default_not_translated

  • CHECK constraints

    переводится, внутри CREATE TABLE

    Встроенные проверки выполняются при загрузке каждой строки.

  • CHECK constraints with functions Oracle lacks

    не создается

    Проверка, которую нельзя записать для Oracle, опускается и попадает в отчет.

    Строка отчета: check_skipped

  • CHECK ... NOT VALID

    не создается

    PostgreSQL не проверял строки, которые были до нее, поэтому они могут ее нарушать.

    Строка отчета: check_not_valid

  • gin, gist, brin and hash indexes, partial and expression indexes, and indexes on CLOB columns

    не создается

    IntoSQL пишет для Oracle индексы b-tree по столбцам, а CLOB Oracle индексировать не может; остальные создайте заново после загрузки.

    Строка отчета: index_skipped

  • foreign keys

    после данных ENABLE NOVALIDATE, затем валидация с предупреждением о строках без родителя

    Ключ сразу проверяет новые строки, а валидация идет последней; при строках без родителя он остается невалидированным с предупреждением, и загрузка не останавливается.

  • ON UPDATE CASCADE, SET NULL or SET DEFAULT; ON DELETE SET DEFAULT

    удаляется

    Внешние ключи в Oracle действуют только при удалении, с CASCADE или SET NULL.

    Строка отчета: foreign_key_action_unsupported

  • foreign keys to tables the dump doesn't create

    не создается

    Ключ не может ссылаться на таблицу, которой нет.

    Строка отчета: foreign_key_skipped

  • names over 128 bytes

    сокращается

    Имена в Oracle 12.2 и новее вмещают 128 байт, больше, чем 63 в PostgreSQL.

    Строка отчета: name_shortened

  • partitioned tables

    одна таблица со строками всех секций

    Строки те же; при необходимости секционируйте таблицу заново в Oracle.

    Строка отчета: partitions_dropped

  • schemas

    одна схема, имя таблицы начинается с имени ее схемы

    Скрипт загружается в схему пользователя, который его запускает, поэтому таблицы из разных схем получают в начале имя своей схемы; таблицы из public сохраняют свои имена.

    Строка отчета: schemas_merged

  • views, functions, procedures, triggers, rules, policies, extensions and types

    перечисляются в отчете

    Их код написан на языке PostgreSQL, поэтому они перечислены, чтобы вы создали их заново.

    Строка отчета: not_converted_objects

  • dumps without CREATE TABLE (--data-only)

    типы определяются по значениям

    Дамп только с данными содержит лишь строки, поэтому типы берутся из значений.

    Строка отчета: no_schema

  • expressions in INSERT values

    NULL

    Конвертер, который не запускает базу данных, не может их вычислить.

    Строка отчета: expression_value

  • types IntoSQL doesn't know

    CLOB

    Тип text вмещает любое значение; измените тип после загрузки.

    Строка отчета: unknown_type

  • text that isn't valid in its encoding

    неверные байты заменяются символом замены

    Скрипт в UTF-8 и загружается с NLS_LANG=.AL32UTF8.

    Строка отчета: invalid_utf8_replaced

  • text decoded twice (ñ)

    копируется как есть

    Дамп содержит именно эти символы, поэтому они копируются; отчет на них указывает.

    Строка отчета: mojibake_suspected

В PostgreSQLВ OracleСтрока отчета
booleanNUMBER(1) с CHECK на 0 и 1В Oracle 21c нет логического типа столбца; 1 и 0 сохраняют true и false во всех версиях.
varchar(n), char(n)VARCHAR2(n CHAR), длина в символахДлины считаются в символах, как в PostgreSQL.
textVARCHAR2(4000 CHAR), если каждое значение укладывается в 4000 байт (750 символов в ключе), иначе CLOBVARCHAR2 по умолчанию вмещает 4000 байт, а Oracle не может индексировать CLOB, поэтому text остается VARCHAR2, если значения помещаются, и становится CLOB, если нет.text_as_lob
empty strings and empty bytea valuesNULLOracle хранит пустую строку как NULL, поэтому значение ' читается обратно как NULL. Отчет подсчитывает их по столбцам.empty_string_null
citextточное сравнениеOracle сравнивает текст точно, если не указано иное, как и text в PostgreSQL; столбцы citext перестают игнорировать регистр.case_insensitive_collation
numeric(p,s) past 38 digits; numeric without a precisionNUMBER, если значения укладываются в 38 цифр, иначе VARCHAR2NUMBER в Oracle вмещает 38 цифр при любом масштабе. Более широкие числа хранятся как текст, чтобы каждая цифра осталась точной.decimal_as_text
moneyNUMBER(19,2)В Oracle нет типа money. Сумма хранится как число, без символа валюты, добавленного локалью сервера.type_replaced
intervalVARCHAR2(100 CHAR) с текстом PostgreSQLinterval смешивает месяцы с днями и секундами, которые Oracle хранит в двух разных типах; текст сохраняет каждую часть.type_replaced
arrays (integer[], text[]...)CLOB с массивом JSONВ Oracle нет массивов. Массив JSON сохраняет каждый элемент и NULL, а JSON_TABLE его читает.type_replaced
ranges, geometric types, tsvector, xml, hstore, PostGIS, composite and extension typesVARCHAR2 или CLOB с текстом PostgreSQLВ Oracle нет равнозначного типа, поэтому каждое значение сохраняет текст, записанный PostgreSQL.type_replaced
json, jsonbCLOBФункции JSON в Oracle читают JSON из текста во всех версиях, а CLOB вмещает значения любого размера.
timestampTIMESTAMPtimestamp without time zone - это показание часов, как TIMESTAMP в Oracle. date становится DATE, который хранит и время суток, оно остается полночью.
timestamp with time zoneTIMESTAMP WITH TIME ZONE в UTCКаждое значение записывается в UTC: тот же момент, какое бы смещение ни записал дамп.
timeINTERVAL DAY TO SECOND от полуночиВ Oracle нет типа для времени суток; интервал от полуночи вмещает любое значение, включая 24:00:00.
infinity, -infinity, BC dates and years past 9999NULLСкрипт пишет годы с 1 по 9999, а в Oracle нет бесконечности. Такие значения становятся NULL, а отчет их подсчитывает.date_out_of_range
NaN and Infinity in numericNULLNUMBER в Oracle не поддерживает NaN и Infinity, поэтому такие значения становятся NULL, а отчет их подсчитывает.invalid_value_null
NOT NULL columns with values that become NULLNOT NULL снимаетсяСтолбец, значения которого пришлось заменить на NULL, не может сохранить NOT NULL, иначе загрузка остановится.not_null_relaxed
enum typesVARCHAR2 с CHECK его метокТекст с проверкой хранит те же значения.
enum columns holding values outside their labelsVARCHAR2 без проверкиЗначения, которых нет среди меток, не прошли бы проверку, поэтому она не создается.enum_check_skipped
uuid, inet, cidr, macaddrVARCHAR2В Oracle нет таких типов; текст сохраняет каждое значение.
byteaBLOBБайты остаются байтами.
bit(n), bit varyingNUMBER со значением битов до 63 бит; дальше цифры в текстеБитовые строки хранятся как число, которое составляют их биты.
oid, xid, cidNUMBER(10) с CHECK на неотрицательностьNUMBER вмещает любое значение.
serial, identityGENERATED BY DEFAULT ON NULL AS IDENTITY, продолжается после наибольшего idСкопированные id загружаются как есть, а счетчик продолжается после наибольшего из них или со следующего значения последовательности, смотря что больше.
a second serial or identity column in a tableобычное целоеOracle нумерует один столбец identity на таблицу; любой другой сохраняет свои значения без счетчика.counter_dropped
sequences not owned by a columnCREATE SEQUENCE с достигнутым значениемПоследовательности в Oracle работают так же.
generated columnsвиртуальные столбцыOracle вычисляет генерируемые столбцы только при чтении; столбцы STORED становятся виртуальными с теми же значениями.stored_as_virtual
generated columns with functions Oracle lacksобычный столбец со значениямиФормула, которую нельзя записать для Oracle, оставляет обычный столбец со значениями из дампа и строку в отчете.generated_not_translated
defaults with functions Oracle lacks, and defaults from a sequenceбез значения по умолчаниюЗначение по умолчанию, которое нельзя записать для Oracle, опускается и попадает в отчет.default_not_translated
CHECK constraintsпереводится, внутри CREATE TABLEВстроенные проверки выполняются при загрузке каждой строки.
CHECK constraints with functions Oracle lacksне создаетсяПроверка, которую нельзя записать для Oracle, опускается и попадает в отчет.check_skipped
CHECK ... NOT VALIDне создаетсяPostgreSQL не проверял строки, которые были до нее, поэтому они могут ее нарушать.check_not_valid
gin, gist, brin and hash indexes, partial and expression indexes, and indexes on CLOB columnsне создаетсяIntoSQL пишет для Oracle индексы b-tree по столбцам, а CLOB Oracle индексировать не может; остальные создайте заново после загрузки.index_skipped
foreign keysпосле данных ENABLE NOVALIDATE, затем валидация с предупреждением о строках без родителяКлюч сразу проверяет новые строки, а валидация идет последней; при строках без родителя он остается невалидированным с предупреждением, и загрузка не останавливается.
ON UPDATE CASCADE, SET NULL or SET DEFAULT; ON DELETE SET DEFAULTудаляетсяВнешние ключи в Oracle действуют только при удалении, с CASCADE или SET NULL.foreign_key_action_unsupported
foreign keys to tables the dump doesn't createне создаетсяКлюч не может ссылаться на таблицу, которой нет.foreign_key_skipped
names over 128 bytesсокращаетсяИмена в Oracle 12.2 и новее вмещают 128 байт, больше, чем 63 в PostgreSQL.name_shortened
partitioned tablesодна таблица со строками всех секцийСтроки те же; при необходимости секционируйте таблицу заново в Oracle.partitions_dropped
schemasодна схема, имя таблицы начинается с имени ее схемыСкрипт загружается в схему пользователя, который его запускает, поэтому таблицы из разных схем получают в начале имя своей схемы; таблицы из public сохраняют свои имена.schemas_merged
views, functions, procedures, triggers, rules, policies, extensions and typesперечисляются в отчетеИх код написан на языке PostgreSQL, поэтому они перечислены, чтобы вы создали их заново.not_converted_objects
dumps without CREATE TABLE (--data-only)типы определяются по значениямДамп только с данными содержит лишь строки, поэтому типы берутся из значений.no_schema
expressions in INSERT valuesNULLКонвертер, который не запускает базу данных, не может их вычислить.expression_value
types IntoSQL doesn't knowCLOBТип text вмещает любое значение; измените тип после загрузки.unknown_type
text that isn't valid in its encodingневерные байты заменяются символом заменыСкрипт в UTF-8 и загружается с NLS_LANG=.AL32UTF8.invalid_utf8_replaced
text decoded twice (ñ)копируется как естьДамп содержит именно эти символы, поэтому они копируются; отчет на них указывает.mojibake_suspected

Загрузите скрипт в Oracle

Скрипт создает таблицы, копирует строки, затем добавляет ключи, индексы и внешние ключи, поэтому загрузка идет быстро и останавливается на первой ошибке.

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

Внешние ключи добавляются ENABLE NOVALIDATE, поэтому новые строки проверяются сразу, а затем выполняется валидация. Если старая строка ссылается на несуществующую строку, скрипт сообщает, какое это ограничение, и оставляет его без валидации, а не прерывается на полпути.

Для большого дампа запускайте SQL*Plus на той же машине, где работает база данных. Скрипт фиксирует каждые 100 строк. Пользователю, который его запускает, нужны CREATE TABLE, CREATE SEQUENCE, CREATE TRIGGER и CREATE PROCEDURE, а также квота в его табличном пространстве.

Как создать дамп PostgreSQL для лучшего результата

Конвертер читает то, что есть в дампе. Эти параметры сохраняют в нем больше ваших данных:

  • --format=plain, формат pg_dump по умолчанию, пишет SQL. Архив custom или directory (-Fc, -Fd) превращается в SQL командой pg_restore -f dump.sql, без сервера баз данных.
  • --no-owner --no-privileges исключают роли и права, которые конвертер все равно пропускает, поэтому дамп получается меньше.
  • pg_dumpall сохраняет все базы данных в один файл; имена таблиц каждой базы начинаются с ее имени, поэтому ничего не конфликтует.

Что не конвертируется

Представления, материализованные представления, функции, процедуры, триггеры, правила, политики защиты строк, расширения и типы написаны на собственном языке PostgreSQL. Они перечислены в отчете по имени и никогда не удаляются молча, чтобы вы могли переписать их для Oracle.

Вопросы о PostgreSQL в Oracle

Нет ответа на ваш вопрос? Напишите нам через страницу контактов, и вам ответит человек. Страница контактов

Мой дамп загружается на сервер?

Нет. Конвертер работает в вашем браузере и читает дамп с диска по частям, поэтому он никогда не покидает ваш компьютер. Приложение для компьютера и командная строка тоже конвертируют на вашем компьютере.

Насколько большим может быть дамп?

Конвертер никогда не держит весь дамп в памяти: он читает файл дважды, по диапазонам, и пишет скрипт по ходу чтения. Дампы в несколько гигабайт конвертируются в браузере, в приложении для компьютера и в командной строке.

Читает ли он архивы pg_dump -Fc?

Не напрямую: архив custom - это двоичный формат. pg_restore -f dump.sql dump.backup превращает его в обычный SQL, который читает конвертер, без подключения к серверу. Получив архив, конвертер так и сообщает.

Из каких версий PostgreSQL может быть дамп?

Дампы, записанные pg_dump и pg_dumpall из PostgreSQL 10 и новее, со строками COPY или INSERT. Каждый тестовый дамп создан в PostgreSQL 13 и 18 и сверен с источником ячейка за ячейкой.

Сохраняются ли значения serial и identity?

Да. Столбец serial или identity каждой таблицы становится столбцом identity, каждая строка сохраняет свой id, а следующий id идет после наибольшего. Последовательности, не принадлежащие столбцу, становятся последовательностями Oracle с достигнутым значением.

Что происходит с массивами, enum и другими типами PostgreSQL?

Массивы становятся массивами JSON, логические значения - NUMBER(1), timestamp с часовым поясом - TIMESTAMP WITH TIME ZONE, а text - VARCHAR2, если значения помещаются, иначе CLOB. Пустые строки становятся NULL, как их хранит Oracle, а отчет их подсчитывает.

А функции, триггеры и представления?

Они перечислены в отчете по имени и не конвертируются, потому что каждая база данных пишет их на своем языке.

На каких версиях Oracle загружается скрипт?

Oracle 19c и новее. Каждый тестовый дамп загружается на 21c и 23ai и останавливается на первой ошибке, если что-то пойдет не так.