Migration d'Oracle vers Postgres étape par étape : Le schéma SH (partitions, vues matérialisées, index bitmap)

Le schéma SH (Sales History) d'Oracle est l'exemple standard d'un entrepôt de données en schéma étoile d'Oracle.

Elle possède une table de faits centrale "Sales", six tables de dimensions, deux vues matérialisées, des tables de faits partitionnées par intervalle et des index bitmap sur des colonnes de faible cardinalité.

Toutes ces fonctionnalités existent dans chaque entrepôt de données Oracle réel.

Et toutes elles exigent des décisions qui, sans détour ora2pg Exécuter ne fera pas pour vous.

Ce post décrit les étapes exactes que j'ai suivies pour migrer SH d'Oracle 19c vers PostgreSQL 18 en utilisant ora2pg.

Le schéma d'exemple SH d'Oracle est disponible sur GitHub.


Qu'est-ce que SH contient

SH modélise un entrepôt de données de vente au détail.

Tables de dimension CANAUX, PAYS, CLIENTS, PRODUITS, PROMOTIONS, HEURES

Tables de faits : VENTES, COÛTS — tous deux partitionnés par plage selon TIME_ID

Vues matérialisées : CAL_MOIS_VENTES_MV, FWEEK_PSCAT_VENTES_MV

Volume de données :

TableauRangées
CANAUX5
Pays23
PROMOTIONS503
PRODUITS72
Temps1,826
CLIENTS55,500
COÛTS82,112
VENTES918,843
DONNÉES_DÉMOGRAPHIQUES_SUPPLÉMENTAIRES4,500

La table SALES, avec près d'un million de lignes, fait en sorte que l'exportation et le chargement COPY prennent 15 à 25 minutes.

Planifiez cela avant de commencer.


Problème 1 : Les tables partitionnées nécessitent deux exportations distinctes

SALES et COSTS sont partitionnés par plage selon TIME_ID dans Oracle.

PostgreSQL prend en charge le partitionnement déclaratif par intervalle avec la même sémantique.

Le problème est que ora2pg diviser ceci en deux types d'exportation.

Le TABLE export génère le DDL de la table parente — le PARTITION BY RANGE (time_id) déclaration — mais pas de tables de partition enfant.

Si vous chargez ceci et essayez d'insérer des lignes, PostgreSQL renvoie :

ERREUR : aucune partition de la relation " sales " trouvée pour la ligne

Les tables enfants partitionnées proviennent d'un Partition exporter.

Vous avez besoin des deux fichiers, et vous devez charger le DDL de la table parente avant les tables filles partitionnées.

Ce que cela ressemble en pratique :

-- TABLE export generates:
CREATE TABLE sh.sales (
  prod_id       bigint NOT NULL,
  cust_id       bigint NOT NULL,
  time_id       date   NOT NULL,
  channel_id    bigint NOT NULL,
  promo_id      bigint NOT NULL,
  quantity_sold numeric(10,2) NOT NULL,
  amount_sold   numeric(10,2) NOT NULL
) PARTITION BY RANGE (time_id);

-- PARTITION export generates:
CREATE TABLE sales_1995 PARTITION OF sh.sales
  FOR VALUES FROM (MINVALUE) TO ('1996-01-01');
CREATE TABLE sales_1996 PARTITION OF sh.sales
  FOR VALUES FROM ('1996-01-01') TO ('1997-01-01');
-- ... 33 more partitions

Le schéma SH a 28 partitions SALES et 9 partitions COSTS, soit 37 au total.

Vérifiez que le compte correspond à Oracle avant le chargement.


Problème 2 : Les colonnes NUMBER(38) ne sont pas gérées par le type PG_INTEGER_TYPE

Chaque colonne ID entière dans SH est déclarée comme nue NOMBRE dans Oracle, qui stocke en interne sous forme de NOMBRE(38).

L'option ora2pg PG_INTEGER_TYPE 1 couvre brut NOMBRE à grand entier.

Mais NOMBRE(38) possède une précision explicite - ora2pg le traite comme un type numérique, pas un entier, et le mappe sur numérique(38).

A numérique(38) la colonne fonctionne mais est inappropriée pour une colonne de clé primaire ou de clé étrangère.

C'est 8 fois plus grand que grand entier, plus lent à indexer, et impossible à utiliser avec des opérations spécifiques aux entiers.

La correction est MODIFICATION_TYPE — une directive sur une seule ligne dans ora2pg.conf qui force un type spécifique pour des colonnes spécifiques.

Règle critique : MODIFICATION_TYPE est une seule ligne.

Toutes les substitutions se font sur une ligne, séparées par des virgules.

Si vous écrivez plusieurs MODIFICATION_TYPE lignes, ora2pg n'utilise que le premier et ignore silencieusement le reste.

Exécuter AFFICHER_COLONNE le premier à identifier tout numérique(38) colonne :

ora2pg -t SHOW_COLUMN > /home/fernando/ora2pg-sh/output/columns-sh.txt
grep 'numeric(38)' /home/fernando/ora2pg-sh/output/columns-sh.txt

Puis ajoutez toutes les substitutions à ora2pg.conf sur une seule ligne :

MODIFY_TYPE  CUSTOMERS:CUST_ID:bigint,CUSTOMERS:CUST_CITY_ID:bigint,CUSTOMERS:CUST_STATE_PROVINCE_ID:bigint,CUSTOMERS:CUST_TOTAL_ID:bigint,CUSTOMERS:CUST_SRC_ID:bigint,CHANNELS:CHANNEL_ID:bigint,CHANNELS:CHANNEL_CLASS_ID:bigint,CHANNELS:CHANNEL_TOTAL_ID:bigint,COUNTRIES:COUNTRY_ID:bigint,COUNTRIES:COUNTRY_SUBREGION_ID:bigint,COUNTRIES:COUNTRY_REGION_ID:bigint,COUNTRIES:COUNTRY_TOTAL_ID:bigint,PRODUCTS:PROD_ID:bigint,PRODUCTS:PROD_SUBCATEGORY_ID:bigint,PRODUCTS:PROD_CATEGORY_ID:bigint,PRODUCTS:SUPPLIER_ID:bigint,PRODUCTS:PROD_TOTAL_ID:bigint,PRODUCTS:PROD_SRC_ID:bigint,PROMOTIONS:PROMO_ID:bigint,PROMOTIONS:PROMO_SUBCATEGORY_ID:bigint,PROMOTIONS:PROMO_CATEGORY_ID:bigint,PROMOTIONS:PROMO_TOTAL_ID:bigint,SALES:PROD_ID:bigint,SALES:CUST_ID:bigint,SALES:CHANNEL_ID:bigint,SALES:PROMO_ID:bigint,COSTS:PROD_ID:bigint,COSTS:PROMO_ID:bigint,COSTS:CHANNEL_ID:bigint

Avant d'exécuter toute exportation, assurez-vous qu'il n'y a qu'un seul élément actif MODIFICATION_TYPE ligne :

grep 'MODIFY_TYPE' /etc/ora2pg/ora2pg.conf | grep -v '^\s*#' | wc -l
# Must be: 1

Problème 3 : Index Bitmap — Conserver, Supprimer ou Remplacer

Oracle utilise des index bitmap sur des colonnes à faible cardinalité dans SH : CHANNEL_ID (5 valeurs), PROMO_ID (503 valeurs) et TIME_ID.

PostgreSQL n'a pas de type d'index bitmap.

Par défaut, ora2pg convertit les index bitmap en index GIN (BITMAP_AS_GIN 1).

C'est incorrect pour ces colonnes — GIN est conçu pour la recherche plein texte et les données de tableaux, pas pour les clés étrangères entières.

Ensemble BITMAP_AS_GIN 0 en ora2pg.conf.

Ceci indique à ora2pg de convertir les index bitmap en index B-tree réguliers, que vous examinez ensuite individuellement.

Le tableau de décision pour SH :

Index bitmap OracleCardinalité de colonneDécision PostgreSQL
canal_de_vente_bix5 chaînesChute — Un arbre B à 5 valeurs n'apporte aucun bénéfice; PostgreSQL utilise un scan séquentiel
ventes_cust_bix55 500 clientsGarder comme arbre B — cardinalité élevée, utilisée pour les requêtes au niveau du client
produit_vente_bix72 produitsGarder comme arbre B
bix_promo_ventes503 promotionsGarder comme arbre B
temps_de_vente_bix1 826 joursRemplacez par BRIN — TIME_ID est ordonné le long d'une table partitionnée par plage
coûts du canal bix5 chaînesChute
coûts_prod_bix, coûts_promo_bixpareilGarder comme arbre B
coûts_temps_bixdates ordonnéesRemplacez par BRIN

BRIN (Block Range Index) est un index de plages de blocs qui stocke les valeurs min/max par bloc de 128 pages.

Pour une colonne de séries temporelles sur une table partitionnée, il est beaucoup moins cher à maintenir qu'un B-tree et tout aussi efficace pour les scans de plage.

Modifier le fichier d'exportation TABLE avant de charger :

-- Delete these two lines:
CREATE INDEX sales_channel_bix ON sh.sales (channel_id);
CREATE INDEX costs_channel_bix ON sh.costs (channel_id);

-- Replace:
CREATE INDEX sales_time_bix ON sh.sales (time_id);
-- With:
CREATE INDEX sales_time_bix ON sh.sales USING BRIN (time_id);

-- Replace:
CREATE INDEX costs_time_bix ON sh.costs (time_id);
-- With:
CREATE INDEX costs_time_bix ON sh.costs USING BRIN (time_id);

Problème 4 : Le rafraîchissement des vues matérialisées n'est plus automatique

Utilisations de CAL_MONTH_SALES_MV et FWEEK_PSCAT_SALES_MV par Oracle REFRESH COMPLET SUR DEMANDE.

Un rafraîchissement complet signifie que la vue est reconstruite à partir de zéro à chaque rafraîchissement.

PostgreSQL ACTUALISER LA VUE MATÉRIALISÉE fait exactement cela — aucun problème de traduction.

Le problème est la planification.

Oracle's DBMS_MVIEW.REFRESH et DBMS_SCHEDULER gérer le rafraîchissement automatique.

PostgreSQL n'a pas d'équivalent intégré.

Après la migration, les vues matérialisées sont des instantanés statiques jusqu'à ce que quelqu'un exécute explicitement :

REFRESH MATERIALIZED VIEW sh.cal_month_sales_mv;
REFRESH MATERIALIZED VIEW sh.fweek_pscat_sales_mv;

Pour répliquer l'actualisation planifiée, installez pg_cron (nécessite les superutilisateurs) et créez un travail après la migration.

Il y a aussi une contrainte d'ordre de chargement.

PostgreSQL exécute la requête MV SELECT immédiatement lorsque vous lancez CRÉER UNE VUE MATÉRIALISÉE COMME SELECT.

Si vous chargez le DDL de la MV avant les données, la requête s'exécute sur des tables vides et la MV est créée vide.

Chargez les données d'abord, puis les MV.

Il y a aussi un problème de `search_path`.

ora2pg n'ajoute pas SET search_path au fichier d'exportation MVIEW.

Si vous chargez SH_mviews.sql directement, psql s'exécute en tant qu'utilisateur du système d'exploitation postgres, dont le search_path par défaut est public — les MV sont créés dans public, pas le.

La solution est le truc du tuyau :

(echo "SET search_path TO sh;"; cat /home/fernando/ora2pg-sh/output/SH_mviews.sql) | \
  sudo -u postgres psql -d shdb

Le écho prépole SET search_path directive alors chaque CRÉER UNE VUE MATÉRIALISÉE dans le fichier arrive dans le le schéma.


Problème 5 : Les objets dimensionnels sont silencieusement abandonnés

Oracle prend en charge CRÉER DIMENSION syntaxe, qui définit les hiérarchies de cumul pour le moteur de réécriture de requêtes et les opérations OLAP d'Oracle.

PostgreSQL n'a pas d'équivalent.

ora2pg ignore silencieusement les objets DIMENSION.

Il n'y a pas d'avertissement, aucune erreur, aucun DDL fragmentaire.

Si vous ne les contrôlez pas manuellement, vous ne saurez pas qu'ils existaient jusqu'à ce que quelqu'un vous demande pourquoi un rapport côté Oracle qui utilisait la réécriture de requête ne fonctionne plus comme prévu.

Dans le schéma SH, CUSTOMERS_DIM, PRODUCTS_DIM, TIMES_DIM et CHANNELS_DIM sont tous ignorés.

Il n'y a aucun impact fonctionnel sur les requêtes SQL standard — les dimensions sont des métadonnées de l'optimiseur, pas des données.

La bonne démarche est de les documenter comme abandonnés dans le registre des risques de migration.


Exécution de la migration : chaque commande dans l'ordre

Environnement

Deux VM sur le même réseau.
srv1 (192.168.0.180) exécute Oracle 19c avec le schéma SH dans PDB pdb1.
srv2 (192.168.0.181) exécute Ubuntu avec PostgreSQL 18 et ora2pg installés.
Toutes les commandes ci-dessous s'exécutent sur srv2 sauf indication contraire.


Étape 1 — Remplir les données Oracle SH à l'aide de SQLcl

Le script d'installation SH appelle sh_populate.sql en interne, qui utilise le SQLcl de CHARGER commande pour lire les fichiers CSV.

SQL*Plus ne prend pas en charge CHARGER — il saute silencieusement chaque CHARGER appel avec SP2-0734 : commande inconnue.

Le schéma est créé mais chaque grande table reste à 0 ligne.

Téléchargez d'abord les fichiers CSV :

# On srv1 (Oracle)
cd /home/oracle/sh

for f in costs customers promotions sales supplementary_demographics times; do
  curl -L -o ${f}.csv \
    "https://raw.githubusercontent.com/oracle-samples/db-sample-schemas/main/sales_history/${f}.csv"
done

ls -lh *.csv
# Expected: 6 files; sales.csv ~74 MB, customers.csv ~13 MB

Exécutez l'installation avec SQLcl, pas avec SQL*Plus :

# On srv1 (Oracle)
cd /home/oracle/sh
sql / as sysdba

Dans SQLcl :

ALTER SESSION SET CONTAINER = pdb1;
@sh_install.sql
-- Prompts:
-- Enter a password for the user SH:             sh
-- Enter a tablespace for SH [USERS]:            (press Enter)
-- Do you want to overwrite the schema? [YES|no]: YES

Vérifier les décomptes de lignes :

# On srv1 (Oracle)
sqlplus sh/sh@//localhost:1521/pdb1 <<'EOF'
SELECT 'SALES',      COUNT(*) FROM sh.sales    UNION ALL
SELECT 'COSTS',      COUNT(*) FROM sh.costs    UNION ALL
SELECT 'CUSTOMERS',  COUNT(*) FROM sh.customers;
EXIT;
EOF
# Expected: SALES 918843, COSTS 82112, CUSTOMERS 55500
# If any large table is 0: SQLcl was not used — re-run using sql, not sqlplus

Étape 2 — Créer la base de données et l'utilisateur PostgreSQL

# On srv2 (PostgreSQL)
sudo -u postgres psql -c "CREATE USER sh WITH PASSWORD 'sh';"
sudo -u postgres psql -c "CREATE DATABASE shdb OWNER sh;"

sudo -u postgres psql -c "\l shdb"
# Expected: one row, owner sh, encoding UTF8

Étape 3 — Créer le répertoire de sortie

# On srv2 (PostgreSQL)
mkdir -p /home/fernando/ora2pg-sh/output

Étape 4 — Configurer ora2pg.conf

# On srv2 (PostgreSQL)
sudo cp /etc/ora2pg/ora2pg.conf /etc/ora2pg/ora2pg.conf.$(date +%Y%m%d)
sudo vi /etc/ora2pg/ora2pg.conf

Définir ces valeurs :

ORACLE_DSN      dbi:Oracle:host=192.168.0.180;sid=pdb1;port=1521
ORACLE_USER     sh
ORACLE_PWD      sh
SCHEMA          SH
OUTPUT_DIR      /home/fernando/ora2pg-sh/output
CREATE_SCHEMA   1
PG_INTEGER_TYPE 1
PG_NUMERIC_TYPE 1
BITMAP_AS_GIN   0

Étape 5 — Exécuter SHOW_COLUMN pour identifier les colonnes NUMBER(38)

# On srv2 (PostgreSQL)
ora2pg -t SHOW_COLUMN > /home/fernando/ora2pg-sh/output/columns-sh.txt
grep 'numeric(38)' /home/fernando/ora2pg-sh/output/columns-sh.txt
# Every column listed here needs a MODIFY_TYPE override to bigint

Étape 6 — Définir les remplacements de `MODIFY_TYPE`

Ajouter le MODIFICATION_TYPE ligne vers ora2pg.conf (doit tenir sur une seule ligne — voir Problème 2 ci-dessus).

Vérifiez qu'une seule ligne active existe :

# On srv2 (PostgreSQL)
grep 'MODIFY_TYPE' /etc/ora2pg/ora2pg.conf | grep -v '^\s*#' | wc -l
# Must be: 1

Étape 7 — Exporter TABLEAU

# On srv2 (PostgreSQL)
ora2pg -t TABLE -o SH_tables.sql 2>&1 | tee /home/fernando/ora2pg-sh/output/table-export-sh.log

ls -lh /home/fernando/ora2pg-sh/output/SH_tables.sql
# Expected: file present, non-zero size

Étape 8 – Corriger l'exportation TABLE (index bitmap)

# On srv2 (PostgreSQL)
cp /home/fernando/ora2pg-sh/output/SH_tables.sql \
   /home/fernando/ora2pg-sh/output/SH_tables_fixed.sql

vi /home/fernando/ora2pg-sh/output/SH_tables_fixed.sql

Appliquez les décisions d'indexation du problème 3 :

  • Supprimer canal_de_vente_bix et coûts du canal bix
  • Remplacer temps_de_vente_bix et coûts_temps_bix avec EN UTILISANT BRIN

Étape 9 — Exporter PARTITION

# On srv2 (PostgreSQL)
ora2pg -t PARTITION -o SH_partitions.sql 2>&1 | tee /home/fernando/ora2pg-sh/output/partition-export-sh.log

grep -c 'PARTITION OF' /home/fernando/ora2pg-sh/output/SH_partitions.sql
# Expected: 37 (28 SALES + 9 COSTS)

Étape 10 — Exporter MVIEW

# On srv2 (PostgreSQL)
ora2pg -t MVIEW -o SH_mviews.sql 2>&1 | tee /home/fernando/ora2pg-sh/output/mview-export-sh.log

Étape 11 — Exporter la VUE

# On srv2 (PostgreSQL)
ora2pg -t VIEW -o SH_view.sql 2>&1 | tee /home/fernando/ora2pg-sh/output/view-export-sh.log

Étape 12 — Exporter les données (COPY)

# On srv2 (PostgreSQL)
ora2pg -t COPY -o SH_data.sql 2>&1 | tee /home/fernando/ora2pg-sh/output/copy-export-sh.log
# Expect 10-20 minutes -- SALES is ~918,000 rows
# Monitor in a second terminal: watch -n 10 "wc -l /home/fernando/ora2pg-sh/output/SH_data.sql"

tail -5 /home/fernando/ora2pg-sh/output/SH_data.sql
# Expected: ends with COMMIT; -- if truncated, the export was interrupted, re-run

Étape 13 — Charger dans le bon ordre

Vérifiez que la cible est vide :

# On srv2 (PostgreSQL)
sudo -u postgres psql -d shdb -c "\dn"
# Expected: no rows (sh schema does not exist yet)

Chargement 1 — DDL de table (tables parentes, index, clés primaires) :

# On srv2 (PostgreSQL)
sudo -u postgres psql -d shdb < /home/fernando/ora2pg-sh/output/SH_tables_fixed.sql
# Expected: CREATE TABLE, CREATE INDEX, ALTER TABLE -- no ERROR lines

sudo -u postgres psql -d shdb -c "\dt sh.*"
# Expected: 9 tables listed

Chargement 2 — Partitionnement des tables enfants (doit précéder les données) :

# On srv2 (PostgreSQL)
sudo -u postgres psql -d shdb < /home/fernando/ora2pg-sh/output/SH_partitions.sql

sudo -u postgres psql -d shdb -c "
SELECT COUNT(*) FROM pg_tables
WHERE schemaname = 'sh'
  AND (tablename LIKE 'sales_%' OR tablename LIKE 'costs_%');"
# Expected: 37

Chargement 3 — Données :

# On srv2 (PostgreSQL)
sudo -u postgres psql -d shdb < /home/fernando/ora2pg-sh/output/SH_data.sql
# Single transaction -- if any COPY fails, everything rolls back
# Expect several minutes for SALES

Chargement 4 — Vues matérialisées (après les données, avec astuce de pipe search_path) :

# On srv2 (PostgreSQL)
(echo "SET search_path TO sh;"; cat /home/fernando/ora2pg-sh/output/SH_mviews.sql) | \
  sudo -u postgres psql -d shdb

sudo -u postgres psql -d shdb -c "\dm sh.*"
# Expected: cal_month_sales_mv, fweek_pscat_sales_mv, both ispopulated = t

Charger 5 — Voir :

# On srv2 (PostgreSQL)
sudo -u postgres psql -d shdb < /home/fernando/ora2pg-sh/output/SH_view.sql

sudo -u postgres psql -d shdb -c "\dv sh.*"
# Expected: sh | profits | view

Étape 14 — Réappliquer les contraintes FK

Le fichier de données COPY d'ora2pg supprime toutes les contraintes FK avant de charger les données et ne les réajoute pas.

C'est un comportement confirmé de ora2pg (problème #1960).

Vérifier que les clés étrangères sont manquantes après le chargement :

# On srv2 (PostgreSQL)
sudo -u postgres psql -d shdb -c "
SELECT COUNT(*) FROM information_schema.table_constraints
WHERE constraint_schema = 'sh'
  AND constraint_type   = 'FOREIGN KEY';"
# Expected: 0

Réappliquer les 10 FK en une seule commande :

# On srv2 (PostgreSQL)
grep '^ALTER TABLE.*FOREIGN KEY' /home/fernando/ora2pg-sh/output/SH_tables.sql | \
  sudo -u postgres psql -d shdb
# Expected: 10 ALTER TABLE lines, no ERROR lines

Vérifier :

# On srv2 (PostgreSQL)
sudo -u postgres psql -d shdb -c "
SELECT COUNT(*) FROM information_schema.table_constraints
WHERE constraint_schema = 'sh'
  AND constraint_type   = 'FOREIGN KEY';"
# Expected: 10

À quoi ressemble le rapport final de TEST

Activer PG_DSN dans ora2pg.conf, puis exécuter :

# On srv2 (PostgreSQL)
ora2pg -t TEST 2>&1 | tee /home/fernando/ora2pg-sh/output/SH_test.txt

Diffs attendus (pas d'erreurs de migration) :

Incompatibilité de compte de clé primaire sur CHANNELS, COUNTRIES, PRODUCTS (Oracle : 0, PostgreSQL : 1) :

Le schéma SH d'Oracle définit les clés primaires avec Désactiver NOVALIDATE — les contraintes sont définies mais pas appliquées.

Le catalogue d'Oracle les signale comme 0 clés primaires actives.

PostgreSQL les applique correctement.

Il s'agit d'un artefact de la conception du schéma d'exemple d'Oracle — aucune action n'est nécessaire.

Incompatibilité de comptage NOT NULL sur COSTS et SALES (Oracle : 6 et 7, PostgreSQL : 0)

Les deux sont des tables partitionnées.

PostgreSQL applique les contraintes NOT NULL aux tables enfants de partition, et non pas à la table parente.

Le test ora2pg lit le parent et obtient 0.

Les contraintes existent et sont appliquées — vérifiez avec \d sh.sales_1995 si nécessaire.

Nombre MVIEW (2 contre 0) :

Le TEST d'ora2pg ne détecte pas de manière fiable les vues matérialisées dans le schéma cible.

Vérifier directement :

# On srv2 (PostgreSQL)
sudo -u postgres psql -d shdb -c "\dm sh.*"
# Expected: both MVs present with ispopulated = t
# If they appear: ignore the TEST counter

Nombre de lignes — toutes les tables doivent correspondre :

CHAÎNES 5 / 5 MATCH
PAYS 23 / 23 MATCH
CLIENTS 55500 / 55500 MATCH
PRODUITS 72 / 72 MATCH
PROMOTIONS 503 / 503 MATCH
TEMPS 1826 / 1826 MATCH
COÛTS 82112 / 82112 MATCH
VENTES 918843 /918843 MATCH
SUPPLEMENTARY_DEMOGRAPHICS 4500 / 4500 MATCH

Si SALES présente une anomalie, vérifiez le nombre de lignes de partitions individuelles :

# On srv2 (PostgreSQL)
sudo -u postgres psql -d shdb -c "
SELECT tableoid::regclass AS partition, COUNT(*)
FROM   sh.sales
GROUP  BY tableoid::regclass
ORDER  BY tableoid::regclass;"
# Look for partitions with 0 rows -- those indicate a load failure for that time range

En résumé

La plupart des schémas d'entrepôt de données Oracle ressemblent à SH.

Tables de faits partitionnées, index bitmap sur les colonnes FK, vues matérialisées reconstruites selon un calendrier, et métadonnées de dimension que personne n'a consultées depuis des années.

Aucun de ceux-ci n'est un blocage - ils ont tous des équivalents PostgreSQL clairs.

Mais la migration n'est pas automatique.

ora2pg vous emmène à 80% du chemin.

Les 20% restants constituent un ensemble de décisions spécifiques : exécuter TABLE et PARTITION comme des exportations distinctes et les charger dans le bon ordre ; remplacer chaque colonne NUMBER(38) par bigint en utilisant MODIFY_TYPE ; définir BITMAP_AS_GIN à 0 et examiner chaque index individuellement ; préfixer SET search_path lors du chargement des vues matérialisées ; et réappliquer les contraintes FK après le chargement des données car ora2pg les supprime et ne les remet jamais.

Les objets DIMENSION sont les seuls à ne pas avoir d'équivalent PostgreSQL.

Ce sont des métadonnées d'optimiseur — leur suppression n'a aucun effet sur la correction de la requête.

Documentez-les comme abandonnés et passez à autre chose.

Dans chaque migration de classes SH que j'exécute, les sept mêmes problèmes se présentent.

Les connaître avant la première exportation est ce qui sépare une transition nette d'une journée de débogage.


Prochaines étapes

Si vous avez un schéma d'entrepôt de données Oracle et que vous souhaitez comprendre la portée de la migration avant de vous engager dans une évaluation complète, commencez par le audit de migration gratuit sur rootfan.com/services/.

Une réponse sur “Oracle to Postgres Migration Step by Step: The SH Schema (Partitions, Materialized Views, Bitmap Indexes)”

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *