Konwertuj PostgreSQL do Oracle
Upuść zwykły zrzut z pg_dump lub pg_dumpall jako .sql lub .sql.gz i pobierz skrypt Oracle z tymi samymi tabelami, kluczami, indeksami i danymi. Każda wartość, której Oracle nie może przechować bez zmian, i każdy obiekt, którego nie przenosi, są wymienione w raporcie.
18 wierszy przekonwertowanych, 2 poprawki, brak odrzuconych
Tabele w pliku shop_2024_postgres.sqlZrzut PostgreSQL 18.6 · zapisany przez pg_dump · utf8mb4
customerszapisana jako CUSTOMERS8 wierszy1
8 kolumn ID NUMBER(10) not null NAME VARCHAR2(4000 CHAR) not null EMAIL VARCHAR2(750 CHAR) not null COUNTRY VARCHAR2(2 CHAR) not null IS_ACTIVE NUMBER(1) not null TAGS CLOB not null SIGNED_UP DATE CREATED_AT TIMESTAMP(6) WITH TIME ZONE not null orderszapisana jako ORDERS10 wierszy1
10 kolumn ID NUMBER(10) not null CUSTOMER_ID NUMBER(10) not null STATUS VARCHAR2(9 CHAR) not null TOTAL NUMBER(10,2) not null ITEMS NUMBER(5) not null ORDERED_AT TIMESTAMP(6) not null SHIPPED_AT TIMESTAMP(6) NOTE VARCHAR2(4000 CHAR) DETAILS CLOB DELIVERY VARCHAR2(100 CHAR)
Nieprzekonwertowane
- Widok paid_orders
Każda baza danych zapisuje je we własnym języku. Przepisz je dla 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;
Poprawki
- "tags" w tabeli customers to text[], czego Oracle nie ma, więc ma typ CLOB z tymi samymi wartościami.
- "delivery" w tabeli orders to interval, czego Oracle nie ma, więc ma typ VARCHAR2(100 CHAR) z tymi samymi wartościami.
Do sprawdzenia
- Widoki, wyzwalacze, procedury i zdarzenia nie są konwertowane: 1 obiekt, każdy wymieniony w raporcie. Utwórz je ręcznie w Oracle.
- Wartości domyślnej "tags" w tabeli customers nie da się zapisać dla Oracle (a value its type can't hold), więc kolumna nie ma wartości domyślnej. Dodaj wartość domyślną ręcznie, jeśli nowe wiersze jej potrzebują.
- Indeks idx_orders_details w tabeli orders używa wyrażenia, którego nie da się zapisać dla Oracle (a gin index), więc go nie utworzono. Utwórz indeks ręcznie, jeśli zapytania go potrzebują.
- W "shipped_at" tabeli orders 1 data poza zakresem Oracle, np. infinity, staje się NULL. Oracle przechowuje lata od 1 do 9999 bez nieskończoności; jeśli te daty są ważne, zastąp je w źródle.
Plik jest konwertowany na tym komputerze i nigdy nie jest wysyłany.
Inne przykłady:
Co zmienia się między PostgreSQL a Oracle
Każdy wiersz to reguła stosowana przez konwerter, z wpisem, który dodaje do raportu, gdy reguła coś zmienia w twoich danych.
boolean
NUMBER(1) z CHECK 0 i 1
Oracle 21c nie ma typu kolumny logicznej; 1 i 0 zachowują true i false w każdej wersji.
varchar(n), char(n)
VARCHAR2(n CHAR), liczony w znakach
Długości liczą znaki, tak jak w PostgreSQL.
text
VARCHAR2(4000 CHAR), gdy każda wartość mieści się w 4000 bajtach (750 znakach w kluczu), w przeciwnym razie CLOB
VARCHAR2 mieści domyślnie 4000 bajtów, a Oracle nie indeksuje CLOB, więc tekst zostaje VARCHAR2, gdy jego wartości się mieszczą, a CLOB, gdy nie.
Wpis w raporcie: text_as_lob
empty strings and empty bytea values
NULL
Oracle zapisuje pusty ciąg jako NULL, więc wartość ' jest odczytywana jako NULL. Raport liczy je w każdej kolumnie.
Wpis w raporcie: empty_string_null
citext
dokładne porównanie
Oracle porównuje tekst dokładnie, chyba że ustawisz inaczej, jak text w PostgreSQL; kolumny citext przestają ignorować wielkość liter.
Wpis w raporcie: case_insensitive_collation
numeric(p,s) past 38 digits; numeric without a precision
NUMBER, gdy wartości mieszczą się w 38 cyfrach, w przeciwnym razie VARCHAR2
NUMBER w Oracle mieści 38 cyfr przy dowolnej skali. Szersze liczby są zapisywane jako tekst, aby każda cyfra pozostała dokładna.
Wpis w raporcie: decimal_as_text
money
NUMBER(19,2)
Oracle nie ma typu money. Kwota jest zapisywana jako liczba, bez symbolu waluty dodanego przez ustawienia regionalne serwera.
Wpis w raporcie: type_replaced
interval
VARCHAR2(100 CHAR) z tekstem PostgreSQL
interval łączy miesiące z dniami i sekundami, które Oracle trzyma w dwóch różnych typach; tekst zachowuje każdą część.
Wpis w raporcie: type_replaced
arrays (integer[], text[]...)
CLOB z tablicą JSON
Oracle nie ma tablic. Tablica JSON zachowuje każdy element i NULL, a JSON_TABLE ją odczytuje.
Wpis w raporcie: type_replaced
ranges, geometric types, tsvector, xml, hstore, PostGIS, composite and extension types
VARCHAR2 lub CLOB z tekstem PostgreSQL
Oracle nie ma odpowiedniego typu, więc każda wartość zachowuje tekst zapisany przez PostgreSQL.
Wpis w raporcie: type_replaced
json, jsonb
CLOB
Funkcje JSON w Oracle odczytują JSON z tekstu w każdej wersji, a CLOB mieści wartości dowolnego rozmiaru.
timestamp
TIMESTAMP
timestamp bez strefy czasowej to wskazanie zegara, jak TIMESTAMP w Oracle. date staje się DATE, który zawiera też godzinę, ustawioną na północ.
timestamp with time zone
TIMESTAMP WITH TIME ZONE w UTC
Każda wartość jest zapisywana w UTC, ten sam moment niezależnie od przesunięcia w zrzucie.
time
INTERVAL DAY TO SECOND od północy
Oracle nie ma typu godziny dnia; interwał od północy mieści każdą wartość, łącznie z 24:00:00.
infinity, -infinity, BC dates and years past 9999
NULL
Skrypt zapisuje lata od 1 do 9999, a Oracle nie ma nieskończoności. Te wartości stają się NULL, a raport je liczy.
Wpis w raporcie: date_out_of_range
NaN and Infinity in numeric
NULL
NUMBER w Oracle nie ma NaN ani Infinity, więc te wartości stają się NULL, a raport je liczy.
Wpis w raporcie: invalid_value_null
NOT NULL columns with values that become NULL
NOT NULL usunięte
Kolumna, której wartości musiały stać się NULL, nie może zachować NOT NULL, bo ładowanie by się zatrzymało.
Wpis w raporcie: not_null_relaxed
enum types
VARCHAR2 z CHECK jego etykiet
Tekst z ograniczeniem CHECK zachowuje te same wartości.
enum columns holding values outside their labels
VARCHAR2 bez CHECK
Wartości spoza etykiet nie przeszłyby ograniczenia CHECK, więc nie jest ono tworzone.
Wpis w raporcie: enum_check_skipped
uuid, inet, cidr, macaddr
VARCHAR2
Oracle nie ma takich typów; tekst zachowuje każdą wartość.
bytea
BLOB
Bajty pozostają bajtami.
bit(n), bit varying
NUMBER z wartością bitów do 63 bitów; powyżej cyfry jako tekst
Ciągi bitów są zapisywane jako liczba, którą tworzą ich bity.
oid, xid, cid
NUMBER(10) z CHECK, że wartość nie jest ujemna
NUMBER mieści każdą wartość.
serial, identity
GENERATED BY DEFAULT ON NULL AS IDENTITY, kontynuowane po najwyższym id
Skopiowane id ładują się bez zmian, a licznik idzie dalej od najwyższego z nich lub od następnej wartości sekwencji, zależnie od tego, która jest wyższa.
a second serial or identity column in a table
zwykła liczba całkowita
Oracle liczy jedną kolumnę identity na tabelę; każda inna zachowuje wartości bez licznika.
Wpis w raporcie: counter_dropped
sequences not owned by a column
CREATE SEQUENCE, od osiągniętej wartości
Sekwencje w Oracle działają tak samo.
generated columns
kolumny wirtualne
Oracle oblicza kolumny generowane tylko przy odczycie; kolumny STORED stają się wirtualne z tymi samymi wartościami.
Wpis w raporcie: stored_as_virtual
generated columns with functions Oracle lacks
zwykła kolumna z wartościami
Formuła, której nie da się zapisać dla Oracle, zostawia zwykłą kolumnę z wartościami ze zrzutu i wpis w raporcie.
Wpis w raporcie: generated_not_translated
defaults with functions Oracle lacks, and defaults from a sequence
brak wartości domyślnej
Wartość domyślna, której nie da się zapisać dla Oracle, jest pomijana i zgłaszana w raporcie.
Wpis w raporcie: default_not_translated
CHECK constraints
przetłumaczone, wewnątrz CREATE TABLE
CHECK w definicji tabeli działa przy ładowaniu każdego wiersza.
CHECK constraints with functions Oracle lacks
nie jest tworzone
Ograniczenie CHECK, którego nie da się zapisać dla Oracle, jest pomijane i zgłaszane w raporcie.
Wpis w raporcie: check_skipped
CHECK ... NOT VALID
nie jest tworzone
PostgreSQL nigdy nie sprawdził wierszy, które były wcześniej, więc mogą je łamać.
Wpis w raporcie: check_not_valid
gin, gist, brin and hash indexes, partial and expression indexes, and indexes on CLOB columns
nie jest tworzone
IntoSQL zapisuje dla Oracle indeksy b-tree na kolumnach, a Oracle nie indeksuje CLOB; pozostałe utwórz ponownie po załadowaniu.
Wpis w raporcie: index_skipped
foreign keys
po danych z ENABLE NOVALIDATE, potem walidowany z ostrzeżeniem o osieroconych wierszach
Klucz od razu sprawdza nowe wiersze i jest walidowany na końcu; wiersze bez rodzica zostawiają go niezwalidowanym z ostrzeżeniem, zamiast zatrzymać ładowanie.
ON UPDATE CASCADE, SET NULL or SET DEFAULT; ON DELETE SET DEFAULT
usunięte
Klucze obce w Oracle działają tylko przy usuwaniu, z CASCADE lub SET NULL.
Wpis w raporcie: foreign_key_action_unsupported
foreign keys to tables the dump doesn't create
nie jest tworzone
Klucz nie może wskazywać tabeli, której nie ma.
Wpis w raporcie: foreign_key_skipped
names over 128 bytes
skrócone
Nazwy w Oracle 12.2 i nowszym mają do 128 bajtów, więcej niż 63 w PostgreSQL.
Wpis w raporcie: name_shortened
partitioned tables
jedna tabela z wierszami wszystkich partycji
Wiersze są te same; jeśli trzeba, podziel tabelę na partycje ponownie w Oracle.
Wpis w raporcie: partitions_dropped
schemas
jeden schemat, nazwa każdej tabeli zaczyna się od nazwy jej schematu
Skrypt ładuje się do schematu użytkownika, który go uruchamia, więc tabele z różnych schematów dostają na początku nazwę swojego schematu; tabele z public zachowują nazwy.
Wpis w raporcie: schemas_merged
views, functions, procedures, triggers, rules, policies, extensions and types
wymienione w raporcie
Ich kod jest napisany w języku PostgreSQL, więc trafiają na listę do ręcznego odtworzenia.
Wpis w raporcie: not_converted_objects
dumps without CREATE TABLE (--data-only)
typy odczytane z wartości
Zrzut samych danych zawiera tylko wiersze, więc typy pochodzą z wartości.
Wpis w raporcie: no_schema
expressions in INSERT values
NULL
Konwerter, który nie uruchamia bazy danych, nie może ich obliczyć.
Wpis w raporcie: expression_value
types IntoSQL doesn't know
CLOB
Tekst mieści każdą wartość; zmień typ po załadowaniu.
Wpis w raporcie: unknown_type
text that isn't valid in its encoding
błędne bajty jako znak zastępczy
Skrypt jest w UTF-8 i ładuje się z NLS_LANG=.AL32UTF8.
Wpis w raporcie: invalid_utf8_replaced
text decoded twice (ñ)
skopiowane bez zmian
Zrzut zawiera dokładnie te znaki, więc są kopiowane; raport je wskazuje.
Wpis w raporcie: mojibake_suspected
| W PostgreSQL | W Oracle | Wpis w raporcie |
|---|---|---|
| boolean | NUMBER(1) z CHECK 0 i 1Oracle 21c nie ma typu kolumny logicznej; 1 i 0 zachowują true i false w każdej wersji. | |
| varchar(n), char(n) | VARCHAR2(n CHAR), liczony w znakachDługości liczą znaki, tak jak w PostgreSQL. | |
| text | VARCHAR2(4000 CHAR), gdy każda wartość mieści się w 4000 bajtach (750 znakach w kluczu), w przeciwnym razie CLOBVARCHAR2 mieści domyślnie 4000 bajtów, a Oracle nie indeksuje CLOB, więc tekst zostaje VARCHAR2, gdy jego wartości się mieszczą, a CLOB, gdy nie. | text_as_lob |
| empty strings and empty bytea values | NULLOracle zapisuje pusty ciąg jako NULL, więc wartość ' jest odczytywana jako NULL. Raport liczy je w każdej kolumnie. | empty_string_null |
| citext | dokładne porównanieOracle porównuje tekst dokładnie, chyba że ustawisz inaczej, jak text w PostgreSQL; kolumny citext przestają ignorować wielkość liter. | case_insensitive_collation |
| numeric(p,s) past 38 digits; numeric without a precision | NUMBER, gdy wartości mieszczą się w 38 cyfrach, w przeciwnym razie VARCHAR2NUMBER w Oracle mieści 38 cyfr przy dowolnej skali. Szersze liczby są zapisywane jako tekst, aby każda cyfra pozostała dokładna. | decimal_as_text |
| money | NUMBER(19,2)Oracle nie ma typu money. Kwota jest zapisywana jako liczba, bez symbolu waluty dodanego przez ustawienia regionalne serwera. | type_replaced |
| interval | VARCHAR2(100 CHAR) z tekstem PostgreSQLinterval łączy miesiące z dniami i sekundami, które Oracle trzyma w dwóch różnych typach; tekst zachowuje każdą część. | type_replaced |
| arrays (integer[], text[]...) | CLOB z tablicą JSONOracle nie ma tablic. Tablica JSON zachowuje każdy element i NULL, a JSON_TABLE ją odczytuje. | type_replaced |
| ranges, geometric types, tsvector, xml, hstore, PostGIS, composite and extension types | VARCHAR2 lub CLOB z tekstem PostgreSQLOracle nie ma odpowiedniego typu, więc każda wartość zachowuje tekst zapisany przez PostgreSQL. | type_replaced |
| json, jsonb | CLOBFunkcje JSON w Oracle odczytują JSON z tekstu w każdej wersji, a CLOB mieści wartości dowolnego rozmiaru. | |
| timestamp | TIMESTAMPtimestamp bez strefy czasowej to wskazanie zegara, jak TIMESTAMP w Oracle. date staje się DATE, który zawiera też godzinę, ustawioną na północ. | |
| timestamp with time zone | TIMESTAMP WITH TIME ZONE w UTCKażda wartość jest zapisywana w UTC, ten sam moment niezależnie od przesunięcia w zrzucie. | |
| time | INTERVAL DAY TO SECOND od północyOracle nie ma typu godziny dnia; interwał od północy mieści każdą wartość, łącznie z 24:00:00. | |
| infinity, -infinity, BC dates and years past 9999 | NULLSkrypt zapisuje lata od 1 do 9999, a Oracle nie ma nieskończoności. Te wartości stają się NULL, a raport je liczy. | date_out_of_range |
| NaN and Infinity in numeric | NULLNUMBER w Oracle nie ma NaN ani Infinity, więc te wartości stają się NULL, a raport je liczy. | invalid_value_null |
| NOT NULL columns with values that become NULL | NOT NULL usunięteKolumna, której wartości musiały stać się NULL, nie może zachować NOT NULL, bo ładowanie by się zatrzymało. | not_null_relaxed |
| enum types | VARCHAR2 z CHECK jego etykietTekst z ograniczeniem CHECK zachowuje te same wartości. | |
| enum columns holding values outside their labels | VARCHAR2 bez CHECKWartości spoza etykiet nie przeszłyby ograniczenia CHECK, więc nie jest ono tworzone. | enum_check_skipped |
| uuid, inet, cidr, macaddr | VARCHAR2Oracle nie ma takich typów; tekst zachowuje każdą wartość. | |
| bytea | BLOBBajty pozostają bajtami. | |
| bit(n), bit varying | NUMBER z wartością bitów do 63 bitów; powyżej cyfry jako tekstCiągi bitów są zapisywane jako liczba, którą tworzą ich bity. | |
| oid, xid, cid | NUMBER(10) z CHECK, że wartość nie jest ujemnaNUMBER mieści każdą wartość. | |
| serial, identity | GENERATED BY DEFAULT ON NULL AS IDENTITY, kontynuowane po najwyższym idSkopiowane id ładują się bez zmian, a licznik idzie dalej od najwyższego z nich lub od następnej wartości sekwencji, zależnie od tego, która jest wyższa. | |
| a second serial or identity column in a table | zwykła liczba całkowitaOracle liczy jedną kolumnę identity na tabelę; każda inna zachowuje wartości bez licznika. | counter_dropped |
| sequences not owned by a column | CREATE SEQUENCE, od osiągniętej wartościSekwencje w Oracle działają tak samo. | |
| generated columns | kolumny wirtualneOracle oblicza kolumny generowane tylko przy odczycie; kolumny STORED stają się wirtualne z tymi samymi wartościami. | stored_as_virtual |
| generated columns with functions Oracle lacks | zwykła kolumna z wartościamiFormuła, której nie da się zapisać dla Oracle, zostawia zwykłą kolumnę z wartościami ze zrzutu i wpis w raporcie. | generated_not_translated |
| defaults with functions Oracle lacks, and defaults from a sequence | brak wartości domyślnejWartość domyślna, której nie da się zapisać dla Oracle, jest pomijana i zgłaszana w raporcie. | default_not_translated |
| CHECK constraints | przetłumaczone, wewnątrz CREATE TABLECHECK w definicji tabeli działa przy ładowaniu każdego wiersza. | |
| CHECK constraints with functions Oracle lacks | nie jest tworzoneOgraniczenie CHECK, którego nie da się zapisać dla Oracle, jest pomijane i zgłaszane w raporcie. | check_skipped |
| CHECK ... NOT VALID | nie jest tworzonePostgreSQL nigdy nie sprawdził wierszy, które były wcześniej, więc mogą je łamać. | check_not_valid |
| gin, gist, brin and hash indexes, partial and expression indexes, and indexes on CLOB columns | nie jest tworzoneIntoSQL zapisuje dla Oracle indeksy b-tree na kolumnach, a Oracle nie indeksuje CLOB; pozostałe utwórz ponownie po załadowaniu. | index_skipped |
| foreign keys | po danych z ENABLE NOVALIDATE, potem walidowany z ostrzeżeniem o osieroconych wierszachKlucz od razu sprawdza nowe wiersze i jest walidowany na końcu; wiersze bez rodzica zostawiają go niezwalidowanym z ostrzeżeniem, zamiast zatrzymać ładowanie. | |
| ON UPDATE CASCADE, SET NULL or SET DEFAULT; ON DELETE SET DEFAULT | usunięteKlucze obce w Oracle działają tylko przy usuwaniu, z CASCADE lub SET NULL. | foreign_key_action_unsupported |
| foreign keys to tables the dump doesn't create | nie jest tworzoneKlucz nie może wskazywać tabeli, której nie ma. | foreign_key_skipped |
| names over 128 bytes | skróconeNazwy w Oracle 12.2 i nowszym mają do 128 bajtów, więcej niż 63 w PostgreSQL. | name_shortened |
| partitioned tables | jedna tabela z wierszami wszystkich partycjiWiersze są te same; jeśli trzeba, podziel tabelę na partycje ponownie w Oracle. | partitions_dropped |
| schemas | jeden schemat, nazwa każdej tabeli zaczyna się od nazwy jej schematuSkrypt ładuje się do schematu użytkownika, który go uruchamia, więc tabele z różnych schematów dostają na początku nazwę swojego schematu; tabele z public zachowują nazwy. | schemas_merged |
| views, functions, procedures, triggers, rules, policies, extensions and types | wymienione w raporcieIch kod jest napisany w języku PostgreSQL, więc trafiają na listę do ręcznego odtworzenia. | not_converted_objects |
| dumps without CREATE TABLE (--data-only) | typy odczytane z wartościZrzut samych danych zawiera tylko wiersze, więc typy pochodzą z wartości. | no_schema |
| expressions in INSERT values | NULLKonwerter, który nie uruchamia bazy danych, nie może ich obliczyć. | expression_value |
| types IntoSQL doesn't know | CLOBTekst mieści każdą wartość; zmień typ po załadowaniu. | unknown_type |
| text that isn't valid in its encoding | błędne bajty jako znak zastępczySkrypt jest w UTF-8 i ładuje się z NLS_LANG=.AL32UTF8. | invalid_utf8_replaced |
| text decoded twice (ñ) | skopiowane bez zmianZrzut zawiera dokładnie te znaki, więc są kopiowane; raport je wskazuje. | mojibake_suspected |
Załaduj skrypt do Oracle
Skrypt tworzy tabele, kopiuje wiersze, a potem dodaje klucze, indeksy i klucze obce, więc ładuje się szybko i zatrzymuje przy pierwszym błędzie.
NLS_LANG=.AL32UTF8 sqlplus -S -L user@//localhost/mydb @postgresql.oracle.sqlKlucze obce są dodawane z ENABLE NOVALIDATE, więc nowe wiersze są sprawdzane od razu, a potem walidowane. Jeśli stary wiersz wskazuje na nieistniejący wiersz, skrypt podaje, którego ograniczenia to dotyczy, i zostawia je niezwalidowane, zamiast przerwać w połowie.
Przy dużym zrzucie uruchom SQL*Plus na tej samej maszynie co baza danych. Skrypt zatwierdza co 100 wierszy. Użytkownik, który go uruchamia, potrzebuje CREATE TABLE, CREATE SEQUENCE, CREATE TRIGGER i CREATE PROCEDURE oraz limitu w swojej przestrzeni tabel.
Jak zrobić zrzut PostgreSQL dla najlepszego wyniku
Konwerter odczytuje to, co zawiera zrzut. Te opcje zachowują w nim więcej twoich danych:
- --format=plain, domyślny w pg_dump, zapisuje SQL. Archiwum custom lub directory (-Fc, -Fd) zamienisz na SQL poleceniem pg_restore -f dump.sql, bez serwera bazy danych.
- --no-owner --no-privileges pomijają role i uprawnienia, które konwerter i tak pomija, więc zrzut jest mniejszy.
- pg_dumpall zapisuje każdą bazę danych w jednym pliku; nazwy tabel każdej z nich zaczynają się od jej nazwy, więc nic się nie powtarza.
Co nie jest konwertowane
Widoki, widoki zmaterializowane, funkcje, procedury, wyzwalacze, reguły, polityki bezpieczeństwa wierszy, rozszerzenia i typy są pisane we własnym języku PostgreSQL. Raport wymienia je z nazwy i nigdy nie pomija ich po cichu, więc możesz je przepisać dla Oracle.
Pytania o konwersję PostgreSQL do Oracle
Nie ma tu odpowiedzi? Napisz do nas przez stronę kontaktu, a odpowie człowiek. Strona kontaktu
Czy mój zrzut jest wysyłany?
Nie. Konwerter działa w przeglądarce i odczytuje zrzut z dysku po kawałku, więc nigdy nie opuszcza on twojego komputera. Aplikacja na komputer i wiersz poleceń też konwertują na twoim komputerze.
Jak duży może być zrzut?
Konwerter nigdy nie trzyma całego zrzutu w pamięci: czyta plik dwa razy, fragmentami, i zapisuje skrypt na bieżąco. Zrzuty wielogigabajtowe konwertują się w przeglądarce, w aplikacji na komputer i w wierszu poleceń.
Czy odczytuje archiwa pg_dump -Fc?
Nie bezpośrednio: archiwum custom to format binarny. pg_restore -f dump.sql dump.backup zamienia je na zwykły SQL, który czyta konwerter, bez łączenia z serwerem. Konwerter informuje o tym, gdy dostanie archiwum.
Z jakich wersji PostgreSQL może pochodzić zrzut?
Zrzuty zapisane przez pg_dump i pg_dumpall z PostgreSQL od wersji 10, z wierszami COPY lub INSERT. Każdy testowy zrzut pochodzi z PostgreSQL 13 i 18 i jest porównywany ze źródłem komórka po komórce.
Czy wartości serial i identity są zachowywane?
Tak. Kolumna serial lub identity każdej tabeli staje się kolumną identity, każdy wiersz zachowuje swoje id, a kolejne id idzie po najwyższym. Sekwencje niepowiązane z kolumną stają się sekwencjami Oracle od osiągniętej wartości.
Co dzieje się z tablicami, typami enum i innymi typami PostgreSQL?
Tablice stają się tablicami JSON, wartości logiczne NUMBER(1), znaczniki czasu ze strefą TIMESTAMP WITH TIME ZONE, a tekst VARCHAR2, gdy wartości się mieszczą, w przeciwnym razie CLOB. Puste ciągi stają się NULL, bo tak zapisuje je Oracle, a raport je liczy.
A funkcje, wyzwalacze i widoki?
Raport wymienia je z nazwy, ale nie są konwertowane, bo każda baza danych zapisuje je we własnym języku.
Na jakich wersjach Oracle ładuje się skrypt?
Oracle 19c i nowsze. Każdy testowy zrzut ładuje się na 21c i 23ai i zatrzymuje przy pierwszym błędzie, jeśli coś pójdzie nie tak.