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.
Table des matières
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 :
| Tableau | Rangées |
|---|---|
| CANAUX | 5 |
| Pays | 23 |
| PROMOTIONS | 503 |
| PRODUITS | 72 |
| Temps | 1,826 |
| CLIENTS | 55,500 |
| COÛTS | 82,112 |
| VENTES | 918,843 |
| DONNÉES_DÉMOGRAPHIQUES_SUPPLÉMENTAIRES | 4,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 ligneLes 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 Oracle | Cardinalité de colonne | Décision PostgreSQL |
|---|---|---|
| canal_de_vente_bix | 5 chaînes | Chute — Un arbre B à 5 valeurs n'apporte aucun bénéfice; PostgreSQL utilise un scan séquentiel |
| ventes_cust_bix | 55 500 clients | Garder comme arbre B — cardinalité élevée, utilisée pour les requêtes au niveau du client |
| produit_vente_bix | 72 produits | Garder comme arbre B |
| bix_promo_ventes | 503 promotions | Garder comme arbre B |
| temps_de_vente_bix | 1 826 jours | Remplacez par BRIN — TIME_ID est ordonné le long d'une table partitionnée par plage |
| coûts du canal bix | 5 chaînes | Chute |
| coûts_prod_bix, coûts_promo_bix | pareil | Garder comme arbre B |
| coûts_temps_bix | dates ordonnées | Remplacez 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_bixetcoûts du canal bix - Remplacer
temps_de_vente_bixetcoûts_temps_bixavecEN 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 MATCHSi 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)”