{"id":6754,"date":"2026-04-20T14:09:36","date_gmt":"2026-04-20T12:09:36","guid":{"rendered":"https:\/\/rootfan.com\/?p=6754"},"modified":"2026-04-20T14:29:44","modified_gmt":"2026-04-20T12:29:44","slug":"problemes-et-solutions-de-migration-doracle-vers-postgresql","status":"publish","type":"post","link":"https:\/\/rootfan.com\/fr\/oracle-to-postgres-migration-issues-and-fixes\/","title":{"rendered":"Migration Oracle vers Postgres : Probl\u00e8mes et Corrections du Sch\u00e9ma CO"},"content":{"rendered":"<p>Le sch\u00e9ma CO (Customer Orders) d'Oracle est le remplacement moderne de l'ancien sch\u00e9ma OE. <\/p>\n\n\n\n<p>Il est livr\u00e9 avec Oracle 19c, il est activement maintenu et il est construit de la mani\u00e8re dont la plupart des applications Oracle r\u00e9elles sont construites aujourd'hui : colonnes IDENTITY au lieu de paires s\u00e9quence-d\u00e9clencheur, JSON stock\u00e9 dans des colonnes BLOB, et vues qui utilisent des fonctions SQL sp\u00e9cifiques \u00e0 Oracle.<\/p>\n\n\n\n<p>J'ai ex\u00e9cut\u00e9 la migration compl\u00e8te en utilisant ora2pg 25.0 avec Oracle 19c comme source et PostgreSQL 18 comme cible. <\/p>\n\n\n\n<p>Cet article aborde les cinq probl\u00e8mes qui ont n\u00e9cessit\u00e9 une intervention manuelle \u2014 et pourquoi chacun d'eux appara\u00eetra dans presque tous les sch\u00e9mas de production que vous migrerez.<\/p>\n\n\n\n<p>Ceci est le troisi\u00e8me article de la s\u00e9rie. <\/p>\n\n\n\n<p>Le <a href=\"https:\/\/rootfan.com\/fr\/exemple-de-migration-ora2pg\/\">sch\u00e9ma RH post<\/a> mod\u00e8les de d\u00e9clenchement de s\u00e9quence couverts, <code>%TYPE<\/code> param\u00e8tres, et le bug de r\u00e9-application de la FK. <\/p>\n\n\n\n<p>Le <a href=\"https:\/\/rootfan.com\/fr\/migration-doracle-vers-postgresql-avec-ora2pg-etape-par-etape\/\">Sch\u00e9ma de publication du SH<\/a> tables partitionn\u00e9es couvertes, index bitmap et vues mat\u00e9rialis\u00e9es.<\/p>\n\n\n\n<p>CO introduit trois nouvelles cat\u00e9gories de probl\u00e8mes qui n'existent dans aucun des deux.<\/p>\n\n\n\n<!--more-->\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<div class=\"wp-block-rank-math-toc-block\" id=\"rank-math-toc\"><h2>Table des mati\u00e8res<\/h2><nav><ul><li><a href=\"#what-co-contains\">CN est contenu dans<\/a><ul><li><a href=\"#problem-1-identity-columns-have-no-explicit-sequences\">Probl\u00e8me 1 : Les colonnes d'identit\u00e9 n'ont pas de s\u00e9quences explicites<\/a><\/li><li><a href=\"#problem-2-blob-storing-json-must-become-jsonb-not-bytea\">Probl\u00e8me 2 : Stocker du JSON en BLOB doit devenir JSONB, pas BYTEA<\/a><\/li><li><a href=\"#problem-3-fk-columns-are-number38-pks-are-bigint-type-mismatch\">Probl\u00e8me 3 : Les colonnes FK sont NUMBER(38), les PK sont bigint \u2014 Inad\u00e9quation de type<\/a><\/li><li><a href=\"#problem-4-the-is-json-check-constraint-uses-oracle-syntax\">Probl\u00e8me 4 : La contrainte de v\u00e9rification IS JSON utilise la syntaxe Oracle<\/a><\/li><li><a href=\"#problem-5-three-views-use-oracle-specific-sql-functions\">Probl\u00e8me 5 : Trois vues utilisent des fonctions SQL sp\u00e9cifiques \u00e0 Oracle<\/a><ul><li><a href=\"#customer_order_products-listagg-with-on-overflow\">customer_order_products \u2014 LISTAGG avec ON OVERFLOW<\/a><\/li><li><a href=\"#store_orders-grouping_id\">store_orders \u2014 GROUPING_ID<\/a><\/li><li><a href=\"#product_reviews-json_table\">product_reviews \u2014 JSON_TABLE<\/a><\/li><\/ul><\/li><\/ul><\/li><li><a href=\"#running-the-migration-every-command-in-order\">Ex\u00e9cution de la migration\u00a0: chaque commande dans l'ordre<\/a><ul><li><a href=\"#environment\">Environnement<\/a><\/li><li><a href=\"#step-1-gather-oracle-statistics\">\u00c9tape 1 \u2014 Collecter les statistiques Oracle<\/a><\/li><li><a href=\"#step-2-create-the-output-directory\">\u00c9tape 2 \u2014 Cr\u00e9ez le r\u00e9pertoire de sortie<\/a><\/li><li><a href=\"#step-3-base-ora2pg-conf\">\u00c9tape 3 \u2014 Configuration de base ora2pg.conf<\/a><\/li><li><a href=\"#step-4-generate-the-assessment-report\">\u00c9tape 4 \u2014 G\u00e9n\u00e9rer le rapport d'\u00e9valuation<\/a><\/li><li><a href=\"#step-5-analyse-column-types\">\u00c9tape 5 \u2014 Analyser les types de colonnes<\/a><\/li><li><a href=\"#step-6-add-modify_type-and-re-save-the-config\">\u00c9tape 6 \u2014 Ajouter MODIFY_TYPE et r\u00e9enregistrer la configuration<\/a><\/li><li><a href=\"#step-7-run-the-schema-exports\">\u00c9tape 7 \u2014 Ex\u00e9cutez les exports de sch\u00e9ma<\/a><\/li><li><a href=\"#step-8-write-the-manual-fix-files\">\u00c9tape 8 \u2014 R\u00e9diger les fichiers de correctifs manuels<\/a><\/li><li><a href=\"#step-9-export-the-data\">\u00c9tape 9 - Exporter les donn\u00e9es<\/a><\/li><li><a href=\"#step-10-create-the-database\">\u00c9tape 10 \u2014 Cr\u00e9er la base de donn\u00e9es<\/a><\/li><li><a href=\"#step-11-load-in-dependency-order\">\u00c9tape 11 \u2014 Charger dans l'ordre des d\u00e9pendances<\/a><\/li><li><a href=\"#step-12-re-apply-fk-constraints\">\u00c9tape 12 \u2014 R\u00e9appliquer les contraintes FK<\/a><\/li><li><a href=\"#step-13-advance-identity-sequences\">\u00c9tape 13 \u2014 Faire avancer les s\u00e9quences d'IDENTIT\u00c9<\/a><\/li><li><a href=\"#step-14-load-the-views\">\u00c9tape 14 \u2014 Charger les vues<\/a><\/li><li><a href=\"#step-15-run-the-test-comparison\">\u00c9tape 15 \u2014 Ex\u00e9cutez la comparaison TEST<\/a><\/li><li><a href=\"#what-the-final-test-report-looks-like\">\u00c0 quoi ressemble le rapport final de TEST<\/a><\/li><\/ul><\/li><li><a href=\"#summary\">En r\u00e9sum\u00e9<\/a><\/li><\/ul><\/nav><\/div>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"what-co-contains\">CN est contenu dans<\/h2>\n\n\n\n<p>Le <a href=\"https:\/\/github.com\/oracle-samples\/db-sample-schemas\" rel=\"nofollow noopener\" target=\"_blank\">Sch\u00e9ma CO<\/a> Le sch\u00e9ma de r\u00e9f\u00e9rence de gestion des commandes de vente au d\u00e9tail d'Oracle comprend : les clients, les magasins, les produits, les commandes, les exp\u00e9ditions, les articles de commande et les stocks.<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><thead><tr><th>Type d'objet<\/th><th>Compter<\/th><\/tr><\/thead><tbody><tr><td>Tableaux<\/td><td>7<\/td><\/tr><tr><td>Vues<\/td><td>4<\/td><\/tr><tr><td>S\u00e9quences<\/td><td>0 (colonnes d'identit\u00e9 uniquement)<\/td><\/tr><tr><td>Proc\u00e9dures stock\u00e9es<\/td><td>0<\/td><\/tr><tr><td>D\u00e9clencheurs<\/td><td>0<\/td><\/tr><tr><td>Lignes (total)<\/td><td>~8,800<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p>L'absence de proc\u00e9dures stock\u00e9es et de d\u00e9clencheurs ram\u00e8ne l'effort de conversion PL\/SQL \u00e0 z\u00e9ro. La complexit\u00e9 provient de trois points : la fa\u00e7on dont Oracle g\u00e8re les colonnes IDENTITY, une colonne BLOB qui stocke du JSON, et quatre vues \u2014 dont trois utilisent des fonctions SQL sp\u00e9cifiques \u00e0 Oracle que PostgreSQL ne prend pas en charge.<\/p>\n\n\n\n<p>ora2pg \u00e9value le sch\u00e9ma CO <strong>A-3<\/strong>migration directe, estim\u00e9e \u00e0 trois heures-personne. Les facteurs de co\u00fbt sont les trois vues non portables.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"problem-1-identity-columns-have-no-explicit-sequences\">Probl\u00e8me 1 : Les colonnes d'identit\u00e9 n'ont pas de s\u00e9quences explicites<\/h3>\n\n\n\n<p>CO utilise <code>GENERATED BY DEFAULT ON NULL AS IDENTITY<\/code> sur chaque colonne de cl\u00e9 primaire. Oracle g\u00e8re les s\u00e9quences sous-jacentes en interne \u2014 il n'y a pas <code>CR\u00c9ER S\u00c9QUENCE<\/code> instructions dans le sch\u00e9ma, et aucune paire s\u00e9quence-d\u00e9clencheur comme celles utilis\u00e9es par le sch\u00e9ma HR.<\/p>\n\n\n\n<p>ora2pg convertit correctement la syntaxe Oracle : <code>GENERATED BY DEFAULT ON NULL AS IDENTITY<\/code> devient <code>G\u00c9N\u00c9R\u00c9 PAR D\u00c9FAUT COMME IDENTIT\u00c9<\/code> dans PostgreSQL. Cette partie ne n\u00e9cessite aucun travail manuel.<\/p>\n\n\n\n<p>Le probl\u00e8me survient apr\u00e8s le chargement des donn\u00e9es.<\/p>\n\n\n\n<p>Lorsque ora2pg exporte des donn\u00e9es via COPY, il exporte les identifiants de ligne r\u00e9els d'Oracle \u2014 1, 2, 3, \u2026 jusqu'\u00e0 l'ID le plus \u00e9lev\u00e9. Le protocole COPY de PostgreSQL accepte ces valeurs et les ins\u00e8re directement dans les colonnes IDENTITY. La s\u00e9quence IDENTITY n'est pas appel\u00e9e lors d'un chargement COPY. Une fois le chargement termin\u00e9, chaque s\u00e9quence IDENTITY reste \u00e0 sa valeur initiale de 1.<\/p>\n\n\n\n<p>La premi\u00e8re INSERT dans <code>clients<\/code> apr\u00e8s la migration tentera d'utiliser <code>id_client = 1<\/code>. Cette ligne existe d\u00e9j\u00e0. L'insertion \u00e9choue avec <code>la violation de la contrainte unique de la valeur de la cl\u00e9 dupliqu\u00e9e<\/code>.<\/p>\n\n\n\n<p>La correction consiste \u00e0 faire progresser chaque s\u00e9quence IDENTITY au-del\u00e0 de la valeur charg\u00e9e la plus \u00e9lev\u00e9e :<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code\" data-no-translation=\"\"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nSET search_path = co, public;\nSELECT setval(pg_get_serial_sequence(&#039;co.customers&#039;,  &#039;customer_id&#039;),  (SELECT MAX(customer_id)  FROM co.customers));\nSELECT setval(pg_get_serial_sequence(&#039;co.orders&#039;,     &#039;order_id&#039;),     (SELECT MAX(order_id)     FROM co.orders));\nSELECT setval(pg_get_serial_sequence(&#039;co.shipments&#039;,  &#039;shipment_id&#039;),  (SELECT MAX(shipment_id)  FROM co.shipments));\nSELECT setval(pg_get_serial_sequence(&#039;co.stores&#039;,     &#039;store_id&#039;),     (SELECT MAX(store_id)     FROM co.stores));\nSELECT setval(pg_get_serial_sequence(&#039;co.products&#039;,   &#039;product_id&#039;),   (SELECT MAX(product_id)   FROM co.products));\n<\/pre><\/div>\n\n\n<p><code>pg_get_serial_sequence<\/code> r\u00e9sout le nom de la s\u00e9quence \u00e0 partir des noms de table et de colonne. Cela \u00e9vite de coder en dur les noms de s\u00e9quence, qui varient entre les versions d'ora2pg.<\/p>\n\n\n\n<p>Ex\u00e9cutez ce bloc imm\u00e9diatement apr\u00e8s le chargement des donn\u00e9es, avant que toute application n'acc\u00e8de \u00e0 la base de donn\u00e9es migr\u00e9e. Sur un sch\u00e9ma de production, les s\u00e9quences manqu\u00e9es entra\u00eenent des \u00e9checs d'INSERT qui sont faciles \u00e0 manquer lors des tests si l'ensemble de donn\u00e9es de test utilise des identifiants diff\u00e9rents de ceux de production.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"problem-2-blob-storing-json-must-become-jsonb-not-bytea\">Probl\u00e8me 2 : Stocker du JSON en BLOB doit devenir JSONB, pas BYTEA<\/h3>\n\n\n\n<p><code>produits.d\u00e9tails_produit<\/code> est d\u00e9clar\u00e9 comme <code>BLOB<\/code> dans Oracle avec un <code>V\u00c9RIFIER (product_details IS JSON)<\/code> contrainte. <\/p>\n\n\n\n<p>Les bases de donn\u00e9es Oracle avant la version 21c stockent le JSON sous forme de BLOB ou de CLOB \u2014 il n'y a pas de type de donn\u00e9es JSON natif.<\/p>\n\n\n\n<p>ora2pg mappe toutes les colonnes BLOB vers <code>BYTEA<\/code> par d\u00e9faut. <\/p>\n\n\n\n<p>Pour <code>image_produit<\/code> et <code>magasins.logo<\/code>, c'est exact \u2014 ils stockent des donn\u00e9es d'image binaires. <\/p>\n\n\n\n<p>Pour <code>d\u00e9tails du produit<\/code>, c'est faux.<\/p>\n\n\n\n<p><code>BYTEA<\/code> dans PostgreSQL stocke des octets bruts. Il n'y a pas d'op\u00e9rateurs JSON, pas de fonctions JSON, pas de validation \u00e0 l'insertion. Le <code>avis_produits<\/code> voir les requ\u00eates <code>d\u00e9tails du produit<\/code> en JSON en utilisant <code>jsonb_array_elements<\/code> apr\u00e8s la r\u00e9\u00e9criture (voir Probl\u00e8me 5 ci-dessous). Cette fonction n\u00e9cessite une <code>JSONB<\/code> argument \u2014 il \u00e9chouera avec <code>function jsonb_array_elements(bytea) does not exist<\/code> si la colonne est <code>BYTEA<\/code>.<\/p>\n\n\n\n<p>La correction est un <code>MODIFICATION_TYPE<\/code> remplacer avant d'ex\u00e9cuter l'exportation TABLE :<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code\" data-no-translation=\"\"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nMODIFY_TYPE   PRODUCTS:PRODUCT_DETAILS:jsonb\n<\/pre><\/div>\n\n\n<p>Ceci fait partie d'une plus longue <code>MODIFICATION_TYPE<\/code> ligne \u2014 voir l'\u00e9tape 6 ci-dessous pour la directive compl\u00e8te.<\/p>\n\n\n\n<p><code>JSONB<\/code> applique automatiquement un JSON valide \u00e0 chaque insertion. Le <code>V\u00c9RIFIER (product_details IS JSON)<\/code> La contrainte est donc redondante apr\u00e8s le changement de type de colonne. Elle utilise \u00e9galement la syntaxe Oracle \u2014 PostgreSQL n'a pas de <code>EST-CE DU JSON<\/code> pr\u00e9dicat \u2014 il doit donc \u00eatre supprim\u00e9 du DDL export\u00e9 avant le chargement. Voir Probl\u00e8me 4.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"problem-3-fk-columns-are-number38-pks-are-bigint-type-mismatch\">Probl\u00e8me 3 : Les colonnes FK sont NUMBER(38), les PK sont bigint \u2014 Inad\u00e9quation de type<\/h3>\n\n\n\n<p><code>FORCE_IDENTIT\u00c9_BIGINT 1<\/code> causes ora2pg \u00e0 exporter les colonnes de cl\u00e9 primaire IDENTITY comme <code>grand entier<\/code>. <code>PG_INTEGER_TYPE 1<\/code> couvre brut <code>NOMBRE<\/code> colonnes \u00e0 <code>grand entier<\/code>.<\/p>\n\n\n\n<p>Les colonnes FK dans CO \u2014 <code>commandes.client_id<\/code>, <code>commandes.id_magasin<\/code>, <code>orderens.commande_id<\/code>, et d'autres \u2014 sont d\u00e9clar\u00e9s comme <code>NOMBRE(38)<\/code> Dans Oracle. C'est la repr\u00e9sentation interne d'Oracle d'une colonne d'entiers d\u00e9clar\u00e9e sans pr\u00e9cision. Parce que la pr\u00e9cision est explicitement <code>38<\/code>, <code>PG_INTEGER_TYPE 1<\/code> ne s'applique pas. ora2pg les mappe \u00e0 <code>num\u00e9rique(38)<\/code>.<\/p>\n\n\n\n<p>Le r\u00e9sultat : les colonnes de cl\u00e9 primaire sont <code>grand entier<\/code>, les colonnes de cl\u00e9 \u00e9trang\u00e8re qui y font r\u00e9f\u00e9rence sont <code>num\u00e9rique(38)<\/code>. PostgreSQL exige que les colonnes de cl\u00e9 \u00e9trang\u00e8re (FK) et de cl\u00e9 primaire (PK) aient le m\u00eame type. Le chargement du DDL r\u00e9ussit, mais la cr\u00e9ation des contraintes de cl\u00e9 \u00e9trang\u00e8re \u00e9choue :<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code\" data-no-translation=\"\"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nERROR:  foreign key constraint &quot;orders_customer_id_fk&quot; cannot be implemented\nDETAIL:  Key columns &quot;customer_id&quot; and &quot;customer_id&quot; are of incompatible types: numeric and bigint.\n<\/pre><\/div>\n\n\n<p>La correction consiste \u00e0 ajouter toutes les colonnes FK et les autres colonnes enti\u00e8res \u00e0 <code>MODIFICATION_TYPE<\/code>:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code\" data-no-translation=\"\"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nMODIFY_TYPE   INVENTORY:STORE_ID:bigint,INVENTORY:PRODUCT_ID:bigint,INVENTORY:PRODUCT_INVENTORY:integer,ORDERS:CUSTOMER_ID:bigint,ORDERS:STORE_ID:bigint,ORDER_ITEMS:ORDER_ID:bigint,ORDER_ITEMS:LINE_ITEM_ID:integer,ORDER_ITEMS:PRODUCT_ID:bigint,ORDER_ITEMS:QUANTITY:integer,ORDER_ITEMS:SHIPMENT_ID:bigint,SHIPMENTS:STORE_ID:bigint,SHIPMENTS:CUSTOMER_ID:bigint\n<\/pre><\/div>\n\n\n<p><code>MODIFICATION_TYPE<\/code> lit seulement la premi\u00e8re ligne de la directive. Toutes les substitutions, y compris la substitution JSONB du probl\u00e8me 2 et la <code>num\u00e9rique<\/code> substitutions depuis le bas \u2014 doivent \u00eatre sur une seule ligne s\u00e9par\u00e9e par des virgules.<\/p>\n\n\n\n<p>Aussi : n'incluez pas la pr\u00e9cision du type \u00e0 l'int\u00e9rieur <code>MODIFICATION_TYPE<\/code> \u00e9crire <code>num\u00e9rique<\/code>, pas <code>num\u00e9rique(10,2)<\/code>).<\/p>\n\n\n\n<p>La virgule entre parenth\u00e8ses est trait\u00e9e comme un d\u00e9limiteur et interrompt l'analyse.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"problem-4-the-is-json-check-constraint-uses-oracle-syntax\">Probl\u00e8me 4 : La contrainte de v\u00e9rification IS JSON utilise la syntaxe Oracle<\/h3>\n\n\n\n<p>ora2pg exporte les <code>produits_json_c<\/code> contrainte comme :<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code\" data-no-translation=\"\"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nCONSTRAINT products_json_c CHECK (product_details IS JSON)\n<\/pre><\/div>\n\n\n<p>PostgreSQL n'a pas <code>EST-CE DU JSON<\/code> pr\u00e9dicat. Le chargement du DDL de la table \u00e9choue imm\u00e9diatement avec une erreur de syntaxe.<\/p>\n\n\n\n<p>La contrainte est \u00e9galement redondante. <code>JSONB<\/code> dans PostgreSQL valide le JSON \u00e0 chaque insertion. Toute ligne qui \u00e9choue \u00e0 la validation JSON est rejet\u00e9e avant d'atteindre le stockage. Le <code>EST-CE DU JSON<\/code> la contrainte ne fait rien que le type de colonne ne fasse pas d\u00e9j\u00e0.<\/p>\n\n\n\n<p>La solution : supprimer la ligne de contrainte du DDL export\u00e9 avant le chargement.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code\" data-no-translation=\"\"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nsed -i &#039;\/products_json_c\/d&#039; \/home\/fernando\/ora2pg-co\/output\/CO_tables_fixed.sql\n<\/pre><\/div>\n\n\n<p>V\u00e9rifier :<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code\" data-no-translation=\"\"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\ngrep &#039;products_json_c&#039; \/home\/fernando\/ora2pg-co\/output\/CO_tables_fixed.sql\n# Expected: no output\n<\/pre><\/div>\n\n\n<p>Le rapport TEST affichera une DIFF\u00c9RENCE DE CONTRAINTES DE V\u00c9RIFICATION pour le <code>produits<\/code> table \u2014 une contrainte dans Oracle, z\u00e9ro dans PostgreSQL. C'est attendu et correct.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"problem-5-three-views-use-oracle-specific-sql-functions\">Probl\u00e8me 5 : Trois vues utilisent des fonctions SQL sp\u00e9cifiques \u00e0 Oracle<\/h3>\n\n\n\n<p>CO a quatre vues. Une (<code>commandes_produits<\/code>) utilise le SQL standard et se convertit sans modification. Trois utilisent des fonctions qui n'ont pas d'\u00e9quivalent direct dans PostgreSQL :<\/p>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"customer_order_products-listagg-with-on-overflow\"><code>produits_commande_client<\/code> \u2014 LISTAGG avec SURFLUX<\/h4>\n\n\n\n<p>Oracle's <code>LISTAGG(expr, s\u00e9parateur ON OVERFLOW TRUNCATE '...' WITH COUNT)<\/code> concat\u00e8ne les valeurs dans une cha\u00eene d\u00e9limit\u00e9e avec gestion des d\u00e9passements. <\/p>\n\n\n\n<p>PostgreSQL <code>STRING_AGG(expr, s\u00e9parateur ORDER BY ...)<\/code> est l'\u00e9quivalent \u2014 aucune option de d\u00e9bordement.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code\" data-no-translation=\"\"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\n-- Oracle\nLISTAGG(p.product_name, &#039;, &#039; ON OVERFLOW TRUNCATE &#039;...&#039; WITH COUNT)\n  WITHIN GROUP (ORDER BY oi.line_item_id) items\n\n-- PostgreSQL\nSTRING_AGG(p.product_name, &#039;, &#039; ORDER BY oi.line_item_id) items\n<\/pre><\/div>\n\n\n<h4 class=\"wp-block-heading\" id=\"store_orders-grouping_id\"><code>commandes_magasin<\/code> \u2014 GROUPING_ID<\/h4>\n\n\n\n<p>Oracle's <code>GROUPING_ID(col1, col2)<\/code> renvoie un vecteur de bits entier codant quelles colonnes sont agr\u00e9g\u00e9es dans un <code>ENSEMBLES DE GROUPEMENT<\/code> requ\u00eate. <\/p>\n\n\n\n<p>PostgreSQL <code>GROUPEMENT(col1, col2)<\/code> fait exactement la m\u00eame chose \u2014 le nom de la fonction diff\u00e8re, la s\u00e9mantique est identique. <\/p>\n\n\n\n<p>Le remplacement est \u00e0 l'identique.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code\" data-no-translation=\"\"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\n-- Oracle\nCASE grouping_id(store_name, order_status)\n  WHEN 1 THEN &#039;STORE TOTAL&#039;\n  ...\n\n-- PostgreSQL\nCASE grouping(s.store_name, o.order_status)\n  WHEN 1 THEN &#039;STORE TOTAL&#039;\n  ...\n<\/pre><\/div>\n\n\n<h4 class=\"wp-block-heading\" id=\"product_reviews-json_table\"><code>avis_produits<\/code> \u2014 JSON_TABLE<\/h4>\n\n\n\n<p>Oracle's <code>JSON_TABLE<\/code> \u00e9tend un tableau JSON en lignes \u00e0 l'aide d'une projection relationnelle.<\/p>\n\n\n\n<p>PostgreSQL n'a pas <code>JSON_TABLE<\/code> l'\u00e9quivalent avant la version 15 (il a \u00e9t\u00e9 ajout\u00e9 dans PostgreSQL 15 dans le cadre de la norme SQL\/JSON, mais avec une syntaxe diff\u00e9rente). <\/p>\n\n\n\n<p>Pour PostgreSQL 14 et versions ant\u00e9rieures \u2014 ou pour un comportement coh\u00e9rent entre les versions \u2014 r\u00e9\u00e9crire en utilisant <code>jsonb_array_elements<\/code>:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code\" data-no-translation=\"\"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\n-- Oracle\nFROM products p,\n     JSON_TABLE(p.product_details, &#039;$&#039;\n       COLUMNS (NESTED PATH &#039;$.reviews&#x5B;*]&#039;\n         COLUMNS (rating INTEGER PATH &#039;$.rating&#039;,\n                  review VARCHAR2(4000) PATH &#039;$.review&#039;))) r\n\n-- PostgreSQL\nFROM products p,\n     jsonb_array_elements(p.product_details-&gt;&#039;reviews&#039;) AS review_row\n<\/pre><\/div>\n\n\n<p>Dans la version PostgreSQL, chaque \u00e9l\u00e9ment de tableau JSON est expos\u00e9 comme un <code>jsonb<\/code> valeur nomm\u00e9e <code>ligne_avis<\/code>. <\/p>\n\n\n\n<p>Les valeurs de colonne sont extraites avec <code>->><\/code> op\u00e9rateur : <code>review_row-&gt;&gt;'notation'<\/code> et <code>review_row-&gt;&gt;'avis'<\/code>. <\/p>\n\n\n\n<p>Le casting <code>ENTIER<\/code> le champ de note est obligatoire parce que <code>->><\/code> revient toujours <code>texte<\/code>.<\/p>\n\n\n\n<p>Ce remaniement d\u00e9pend de <code>d\u00e9tails du produit<\/code> \u00eatre <code>JSONB<\/code> \u2014 si la colonne est <code>BYTEA<\/code> (Probl\u00e8me 2 non r\u00e9solu), la vue \u00e9choue imm\u00e9diatement.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"running-the-migration-every-command-in-order\">Ex\u00e9cution de la migration\u00a0: chaque commande dans l'ordre<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"environment\">Environnement<\/h3>\n\n\n\n<p>Deux VM sur le m\u00eame r\u00e9seau. <code>srv1<\/code> (192.168.0.180) ex\u00e9cute Oracle 19c avec le sch\u00e9ma CO dans PDB <code>pdb1<\/code>. <code>srv2<\/code> (192.168.0.181) ex\u00e9cute Ubuntu avec PostgreSQL 18 et ora2pg install\u00e9s. Toutes les commandes ci-dessous sont ex\u00e9cut\u00e9es sur <code>srv2<\/code> sauf indication contraire.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"step-1-gather-oracle-statistics\">\u00c9tape 1 \u2014 Collecter les statistiques Oracle<\/h3>\n\n\n\n<p>Courir sur <code>srv1<\/code> Avant de g\u00e9n\u00e9rer le rapport, les estimations d'effort de ora2pg utilisent les statistiques stock\u00e9es d'Oracle ; des statistiques obsol\u00e8tes produisent des comptages inexacts.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code\" data-no-translation=\"\"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\n-- On Oracle (srv1)\nBEGIN\n  DBMS_STATS.GATHER_SCHEMA_STATS(&#039;CO&#039;);\n  DBMS_STATS.GATHER_DICTIONARY_STATS;\nEND;\n\/\n<\/pre><\/div>\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"step-2-create-the-output-directory\">\u00c9tape 2 \u2014 Cr\u00e9ez le r\u00e9pertoire de sortie<\/h3>\n\n\n<div class=\"wp-block-syntaxhighlighter-code\" data-no-translation=\"\"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nmkdir -p \/home\/fernando\/ora2pg-co\/output\n<\/pre><\/div>\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"step-3-base-ora2pg-conf\">\u00c9tape 3 \u2014 Configuration de base ora2pg.conf<\/h3>\n\n\n<div class=\"wp-block-syntaxhighlighter-code\" data-no-translation=\"\"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\n# \/etc\/ora2pg\/ora2pg.conf\nORACLE_DSN    dbi:Oracle:host=192.168.0.180;service_name=pdb1;port=1521\nORACLE_USER   system\nORACLE_PWD    &lt;system password&gt;\nSCHEMA        CO\nEXPORT_SCHEMA 1\nCREATE_SCHEMA 1\nOUTPUT_DIR    \/home\/fernando\/ora2pg-co\/output\n<\/pre><\/div>\n\n\n<p>Utilisation <code>syst\u00e8me<\/code>, pas <code>co<\/code> \u2014 l'exportation COPY se lit <code>v$base de donn\u00e9es<\/code>, ce qui requiert des privil\u00e8ges DBA.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"step-4-generate-the-assessment-report\">\u00c9tape 4 \u2014 G\u00e9n\u00e9rer le rapport d'\u00e9valuation<\/h3>\n\n\n<div class=\"wp-block-syntaxhighlighter-code\" data-no-translation=\"\"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nora2pg -t SHOW_REPORT --estimate_cost --dump_as_html \\\n  &gt; \/home\/fernando\/ora2pg-co\/output\/report-co.html\n<\/pre><\/div>\n\n\n<p>Ouvrir le HTML dans un navigateur. CO devrait marquer <strong>A-3<\/strong>. Les inducteurs de co\u00fbts sont les trois vues non portables.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"step-5-analyse-column-types\">\u00c9tape 5 \u2014 Analyser les types de colonnes<\/h3>\n\n\n<div class=\"wp-block-syntaxhighlighter-code\" data-no-translation=\"\"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nora2pg -t SHOW_COLUMN &gt; \/home\/fernando\/ora2pg-co\/output\/columns-co.txt\n<\/pre><\/div>\n\n\n<p>Colonnes cl\u00e9s \u00e0 v\u00e9rifier :<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><thead><tr><th>Colonne<\/th><th>ora2pg par d\u00e9faut<\/th><th>Type correct<\/th><th>Raison<\/th><\/tr><\/thead><tbody><tr><td><code>PRIX.UNITAIRE_PRODUIT<\/code><\/td><td><code>double pr\u00e9cision<\/code><\/td><td><code>num\u00e9rique<\/code><\/td><td>Mon\u00e9taire \u2014 les nombres \u00e0 virgule flottante provoquent des erreurs d'arrondi<\/td><\/tr><tr><td><code>PRIX_UNITAIRE_Article_COMMANDE<\/code><\/td><td><code>double pr\u00e9cision<\/code><\/td><td><code>num\u00e9rique<\/code><\/td><td>Idem<\/td><\/tr><tr><td><code>PRODUITS.DETAILS_PRODUIT<\/code><\/td><td><code>bytea<\/code><\/td><td><code>jsonb<\/code><\/td><td>Magasins JSON<\/td><\/tr><tr><td><code>COMMANDES.ID_CLIENT<\/code><\/td><td><code>num\u00e9rique(38)<\/code><\/td><td><code>grand entier<\/code><\/td><td>FK \u00e0 <code>grand entier<\/code> PK \u2014 Incompatibilit\u00e9 de type<\/td><\/tr><tr><td>Tous les autres <code>NOMBRE(38)<\/code> colonnes FK\/entier<\/td><td><code>num\u00e9rique(38)<\/code><\/td><td><code>grand entier<\/code> ou <code>entier<\/code><\/td><td>M\u00eame raison<\/td><\/tr><tr><td><code>MAGASINS.LATITUDE<\/code>, <code>MAGASINS.LONGITUDE<\/code><\/td><td><code>double pr\u00e9cision<\/code><\/td><td><code>double pr\u00e9cision<\/code><\/td><td>Coordonn\u00e9es g\u00e9ographiques \u2014 flottant est correct<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"step-6-add-modify_type-and-re-save-the-config\">\u00c9tape 6 \u2014 Ajouter MODIFY_TYPE et r\u00e9enregistrer la configuration<\/h3>\n\n\n\n<p>Tous les remplacements sur une seule ligne \u2014 <code>MODIFICATION_TYPE<\/code> ignore tout ce qui suit la premi\u00e8re ligne. <\/p>\n\n\n\n<p>Ne pas utiliser la pr\u00e9cision du type (\u00e9crire <code>num\u00e9rique<\/code>, pas <code>num\u00e9rique(10,2)<\/code>).<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code\" data-no-translation=\"\"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nMODIFY_TYPE   PRODUCTS:PRODUCT_DETAILS:jsonb,PRODUCTS:UNIT_PRICE:numeric,ORDER_ITEMS:UNIT_PRICE:numeric,INVENTORY:STORE_ID:bigint,INVENTORY:PRODUCT_ID:bigint,INVENTORY:PRODUCT_INVENTORY:integer,ORDERS:CUSTOMER_ID:bigint,ORDERS:STORE_ID:bigint,ORDER_ITEMS:ORDER_ID:bigint,ORDER_ITEMS:LINE_ITEM_ID:integer,ORDER_ITEMS:PRODUCT_ID:bigint,ORDER_ITEMS:QUANTITY:integer,ORDER_ITEMS:SHIPMENT_ID:bigint,SHIPMENTS:STORE_ID:bigint,SHIPMENTS:CUSTOMER_ID:bigint\n<\/pre><\/div>\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"step-7-run-the-schema-exports\">\u00c9tape 7 \u2014 Ex\u00e9cutez les exports de sch\u00e9ma<\/h3>\n\n\n\n<p>CO n'a pas de s\u00e9quences explicites, ni de proc\u00e9dures stock\u00e9es ou de d\u00e9clencheurs. <\/p>\n\n\n\n<p>Seules les exportations de TABLE et de VIEW sont n\u00e9cessaires.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code\" data-no-translation=\"\"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nora2pg -t TABLE -o CO_tables.sql 2&gt;&amp;1 | tee \/home\/fernando\/ora2pg-co\/output\/table-export-co.log\nora2pg -t VIEW  -o CO_views.sql  2&gt;&amp;1 | tee \/home\/fernando\/ora2pg-co\/output\/view-export-co.log\n<\/pre><\/div>\n\n\n<p>Apr\u00e8s l'exportation du TABLEAU, v\u00e9rifiez les colonnes critiques :<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code\" data-no-translation=\"\"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\ngrep -E &#039;product_details|UNIT_PRICE|IDENTITY&#039; \/home\/fernando\/ora2pg-co\/output\/CO_tables.sql\n<\/pre><\/div>\n\n\n<p><code>d\u00e9tails du produit<\/code> doit montrer <code>JSONB<\/code>. Les deux <code>PRIX_UNITAIRE<\/code> les colonnes doivent appara\u00eetre <code>NUM\u00c9RIQUE<\/code>. Les colonnes d'identit\u00e9 doivent afficher <code>G\u00c9N\u00c9R\u00c9 PAR D\u00c9FAUT COMME IDENTIT\u00c9<\/code>. <\/p>\n\n\n\n<p>Si une colonne affiche le mauvais type, la <code>MODIFICATION_TYPE<\/code> la directive n'a pas pris effet \u2014 v\u00e9rifiez la configuration.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"step-8-write-the-manual-fix-files\">\u00c9tape 8 \u2014 R\u00e9diger les fichiers de correctifs manuels<\/h3>\n\n\n\n<p>Deux fichiers de correction ne peuvent pas \u00eatre g\u00e9n\u00e9r\u00e9s \u00e0 partir de la sortie ora2pg.<\/p>\n\n\n\n<p><code>CO_tables_fixed.sql<\/code> \u2014 supprime la contrainte IS JSON :<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code\" data-no-translation=\"\"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\ncp \/home\/fernando\/ora2pg-co\/output\/CO_tables.sql \\\n   \/home\/fernando\/ora2pg-co\/output\/CO_tables_fixed.sql\nsed -i &#039;\/products_json_c\/d&#039; \/home\/fernando\/ora2pg-co\/output\/CO_tables_fixed.sql\n<\/pre><\/div>\n\n\n<p><code>CO_views_fixed.sql<\/code> \u2014 r\u00e9\u00e9crit les trois vues sp\u00e9cifiques \u00e0 Oracle :<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code\" data-no-translation=\"\"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nSET search_path = co,public;\n\n-- product_orders: standard SQL, no changes needed\nCREATE OR REPLACE VIEW product_orders AS\n  SELECT p.product_name, o.order_status,\n         SUM(oi.quantity * oi.unit_price) total_sales,\n         COUNT(*) order_count\n  FROM   orders o\n  JOIN   order_items oi ON o.order_id = oi.order_id\n  JOIN   customers c    ON o.customer_id = c.customer_id\n  JOIN   products p     ON oi.product_id = p.product_id\n  GROUP  BY p.product_name, o.order_status;\n\n-- customer_order_products: LISTAGG \u2192 STRING_AGG\nCREATE OR REPLACE VIEW customer_order_products AS\n  SELECT o.order_id, o.order_tms, o.order_status,\n         c.customer_id, c.email_address, c.full_name,\n         SUM(oi.quantity * oi.unit_price) order_total,\n         STRING_AGG(p.product_name, &#039;, &#039; ORDER BY oi.line_item_id) items\n  FROM   orders o\n  JOIN   order_items oi ON o.order_id = oi.order_id\n  JOIN   customers c    ON o.customer_id = c.customer_id\n  JOIN   products p     ON oi.product_id = p.product_id\n  GROUP  BY o.order_id, o.order_tms, o.order_status,\n            c.customer_id, c.email_address, c.full_name;\n\n-- store_orders: GROUPING_ID() \u2192 GROUPING()\nCREATE OR REPLACE VIEW store_orders AS\n  SELECT CASE grouping(s.store_name, o.order_status)\n           WHEN 1 THEN &#039;STORE TOTAL&#039;\n           WHEN 2 THEN &#039;STATUS TOTAL&#039;\n           WHEN 3 THEN &#039;GRAND TOTAL&#039;\n         END total,\n         s.store_name,\n         COALESCE(s.web_address, s.physical_address) address,\n         s.latitude, s.longitude,\n         o.order_status,\n         COUNT(DISTINCT o.order_id) order_count,\n         SUM(oi.quantity * oi.unit_price) total_sales\n  FROM   stores s\n  JOIN   orders o    ON s.store_id = o.store_id\n  JOIN   order_items oi ON o.order_id = oi.order_id\n  GROUP  BY GROUPING SETS (\n    (s.store_name, COALESCE(s.web_address, s.physical_address), s.latitude, s.longitude),\n    (s.store_name, COALESCE(s.web_address, s.physical_address), s.latitude, s.longitude, o.order_status),\n    o.order_status,\n    ()\n  );\n\n-- product_reviews: JSON_TABLE \u2192 jsonb_array_elements\nCREATE OR REPLACE VIEW product_reviews AS\n  SELECT p.product_name,\n         (review_row-&gt;&gt;&#039;rating&#039;)::INTEGER AS rating,\n         ROUND(\n           AVG((review_row-&gt;&gt;&#039;rating&#039;)::INTEGER)\n             OVER (PARTITION BY p.product_name),\n           2\n         ) avg_rating,\n         review_row-&gt;&gt;&#039;review&#039; AS review\n  FROM   products p,\n         jsonb_array_elements(p.product_details-&gt;&#039;reviews&#039;) AS review_row;\n<\/pre><\/div>\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"step-9-export-the-data\">\u00c9tape 9 - Exporter les donn\u00e9es<\/h3>\n\n\n<div class=\"wp-block-syntaxhighlighter-code\" data-no-translation=\"\"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nora2pg -t COPY -o CO_data.sql 2&gt;&amp;1 | tee \/home\/fernando\/ora2pg-co\/output\/copy-export-co.log\n<\/pre><\/div>\n\n\n<p>Apr\u00e8s l'exportation, v\u00e9rifiez les nouvelles lignes int\u00e9gr\u00e9es dans les donn\u00e9es JSON. ora2pg n'\u00e9chappe pas les caract\u00e8res de nouvelle ligne litt\u00e9raux \u00e0 l'int\u00e9rieur des valeurs de cha\u00eene JSON. <\/p>\n\n\n\n<p>Si applicable <code>d\u00e9tails du produit<\/code> la valeur contient un nouveau saut de ligne, le parseur COPY le traite comme un s\u00e9parateur de ligne et \u00e9choue avec <code>syntaxe d'entr\u00e9e invalide pour le type json<\/code>.<\/p>\n\n\n\n<p>Corriger les nouvelles lignes int\u00e9gr\u00e9es avant le chargement :<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code\" data-no-translation=\"\"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\npython3 &lt;&lt; &#039;EOF&#039;\nimport re\n\nwith open(&#039;\/home\/fernando\/ora2pg-co\/output\/CO_data.sql&#039;, &#039;r&#039;) as f:\n    lines = f.read().split(&#039;\\n&#039;)\n\nresult = &#x5B;]\nin_products = False\npending = None\n\nfor line in lines:\n    if re.match(r&#039;COPY products\\b&#039;, line):\n        in_products = True\n        result.append(line)\n        continue\n    if in_products:\n        if line == &#039;\\\\.&#039;:\n            if pending is not None:\n                result.append(pending)\n                pending = None\n            result.append(line)\n            in_products = False\n            continue\n        if re.match(r&#039;^\\d+\\t&#039;, line):\n            if pending is not None:\n                result.append(pending)\n            pending = line\n        else:\n            pending = (pending + &#039;\\\\n&#039; + line) if pending else line\n    else:\n        result.append(line)\n\nwith open(&#039;\/home\/fernando\/ora2pg-co\/output\/CO_data_fixed.sql&#039;, &#039;w&#039;) as f:\n    f.write(&#039;\\n&#039;.join(result))\nprint(&quot;Done&quot;)\nEOF\n<\/pre><\/div>\n\n\n<p>V\u00e9rifier que le nombre de lignes est correct :<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code\" data-no-translation=\"\"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\ngrep -c $&#039;\\t&#039; \/home\/fernando\/ora2pg-co\/output\/CO_data_fixed.sql\n# Expected: 8783\n<\/pre><\/div>\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"step-10-create-the-database\">\u00c9tape 10 \u2014 Cr\u00e9er la base de donn\u00e9es<\/h3>\n\n\n<div class=\"wp-block-syntaxhighlighter-code\" data-no-translation=\"\"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nsudo -u postgres psql -c &quot;CREATE ROLE co WITH LOGIN PASSWORD &#039;co&#039;;&quot;\nsudo -u postgres psql -c &quot;CREATE DATABASE codb OWNER co;&quot;\nPGPASSWORD=co psql -U co -d codb -h localhost -c &quot;CREATE SCHEMA co;&quot;\n<\/pre><\/div>\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"step-11-load-in-dependency-order\">\u00c9tape 11 \u2014 Charger dans l'ordre des d\u00e9pendances<\/h3>\n\n\n<div class=\"wp-block-syntaxhighlighter-code\" data-no-translation=\"\"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\n# Tables first\nPGPASSWORD=co psql -U co -d codb -h localhost \\\n  -f \/home\/fernando\/ora2pg-co\/output\/CO_tables_fixed.sql\n\n# Data \u2014 FKs are dropped inside the file before load, not re-added after\nPGPASSWORD=co psql -U co -d codb -h localhost \\\n  -f \/home\/fernando\/ora2pg-co\/output\/CO_data_fixed.sql\n<\/pre><\/div>\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"step-12-re-apply-fk-constraints\">\u00c9tape 12 \u2014 R\u00e9appliquer les contraintes FK<\/h3>\n\n\n\n<p>ora2pg's COPY file supprime toutes les contraintes de cl\u00e9 \u00e9trang\u00e8re avant le chargement des donn\u00e9es et ne les r\u00e9int\u00e8gre jamais.<\/p>\n\n\n\n<p>C'est le m\u00eame bug que dans le laboratoire RH (<a href=\"https:\/\/github.com\/darold\/ora2pg\/issues\" rel=\"nofollow noopener\" target=\"_blank\">probl\u00e8me #1960<\/a>).<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code\" data-no-translation=\"\"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\n(echo &quot;SET search_path TO co;&quot;; \\\n grep &#039;ADD CONSTRAINT.*FOREIGN KEY&#039; \\\n   \/home\/fernando\/ora2pg-co\/output\/CO_tables_fixed.sql) | \\\n  PGPASSWORD=co psql -U co -d codb -h localhost\n<\/pre><\/div>\n\n\n<p>V\u00e9rifier le compte :<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code\" data-no-translation=\"\"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nPGPASSWORD=co psql -U co -d codb -h localhost -c &quot;\nSELECT COUNT(*) FROM information_schema.table_constraints\nWHERE constraint_schema = &#039;co&#039; AND constraint_type = &#039;FOREIGN KEY&#039;;&quot;\n# Expected: 9\n<\/pre><\/div>\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"step-13-advance-identity-sequences\">\u00c9tape 13 \u2014 Faire avancer les s\u00e9quences d'IDENTIT\u00c9<\/h3>\n\n\n<div class=\"wp-block-syntaxhighlighter-code\" data-no-translation=\"\"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nPGPASSWORD=co psql -U co -d codb -h localhost &lt;&lt; &#039;EOF&#039;\nSET search_path = co, public;\nSELECT setval(pg_get_serial_sequence(&#039;co.customers&#039;,  &#039;customer_id&#039;),  (SELECT MAX(customer_id)  FROM co.customers));\nSELECT setval(pg_get_serial_sequence(&#039;co.orders&#039;,     &#039;order_id&#039;),     (SELECT MAX(order_id)     FROM co.orders));\nSELECT setval(pg_get_serial_sequence(&#039;co.shipments&#039;,  &#039;shipment_id&#039;),  (SELECT MAX(shipment_id)  FROM co.shipments));\nSELECT setval(pg_get_serial_sequence(&#039;co.stores&#039;,     &#039;store_id&#039;),     (SELECT MAX(store_id)     FROM co.stores));\nSELECT setval(pg_get_serial_sequence(&#039;co.products&#039;,   &#039;product_id&#039;),   (SELECT MAX(product_id)   FROM co.products));\nEOF\n<\/pre><\/div>\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"step-14-load-the-views\">\u00c9tape 14 \u2014 Charger les vues<\/h3>\n\n\n<div class=\"wp-block-syntaxhighlighter-code\" data-no-translation=\"\"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nPGPASSWORD=co psql -U co -d codb -h localhost \\\n  -f \/home\/fernando\/ora2pg-co\/output\/CO_views_fixed.sql\n# Expected: CREATE VIEW \u00d7 4 \u2014 no errors\n<\/pre><\/div>\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"step-15-run-the-test-comparison\">\u00c9tape 15 \u2014 Ex\u00e9cutez la comparaison TEST<\/h3>\n\n\n\n<p>Ajouter une connexion PostgreSQL \u00e0 <code>ora2pg.conf<\/code>:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code\" data-no-translation=\"\"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nPG_DSN    dbi:Pg:dbname=codb;host=localhost;port=5432\nPG_USER   co\nPG_PWD    co\n<\/pre><\/div>\n\n<div class=\"wp-block-syntaxhighlighter-code\" data-no-translation=\"\"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nora2pg -t TEST 2&gt;&amp;1 | tee \/home\/fernando\/ora2pg-co\/output\/CO_test.txt\n<\/pre><\/div>\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"what-the-final-test-report-looks-like\">\u00c0 quoi ressemble le rapport final de TEST<\/h3>\n\n\n\n<p>Apr\u00e8s que les cinq correctifs sont appliqu\u00e9s, <code>ora2pg -t TEST<\/code> devrait afficher une DIFF et tout le reste OK.<\/p>\n\n\n\n<p><strong>VERIFICAR RESTRICCIONES DIF en <code>produits<\/code><\/strong> \u2014 Oracle a une contrainte de v\u00e9rification (<code>produits_json_c<\/code>), PostgreSQL en a z\u00e9ro. <\/p>\n\n\n\n<p>Ceci est attendu : la contrainte a \u00e9t\u00e9 intentionnellement supprim\u00e9e car elle utilise la syntaxe Oracle et fait double emploi avec <code>JSONB<\/code> validation de type de colonne.<\/p>\n\n\n\n<p>Chaque deuxi\u00e8me section \u2014 colonnes, index, PKs, FKs, tables, vues, s\u00e9quences, nombre de lignes \u2014 devrait afficher OK.<\/p>\n\n\n\n<p>Toute autre ligne DIFF est un vrai probl\u00e8me qui doit \u00eatre r\u00e9solu avant la bascule.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"summary\">En r\u00e9sum\u00e9<\/h2>\n\n\n\n<p>CO est une migration plus propre que HR ou SH \u00e0 certains \u00e9gards \u2014 pas de proc\u00e9dures stock\u00e9es, pas de d\u00e9clencheurs, pas d'index bitmap, pas de tables partitionn\u00e9es. Les probl\u00e8mes qu'elle introduit sont d'une nature diff\u00e9rente.<\/p>\n\n\n\n<p>Les s\u00e9quences d'IDENTIT\u00c9 \u00e9chouant apr\u00e8s un chargement de donn\u00e9es sont invisibles jusqu'\u00e0 la premi\u00e8re INSERTION en production \u2014 il n'y a aucun avertissement pendant la migration elle-m\u00eame. <code>d\u00e9finirval<\/code> L'\u00e9tape est facile \u00e0 manquer car le chargement r\u00e9ussit sans probl\u00e8me sans elle.<\/p>\n\n\n\n<p>Le mod\u00e8le BLOB-as-JSON est courant dans les bases de donn\u00e9es Oracle ant\u00e9rieures \u00e0 la version 21c, o\u00f9 il n'existait pas de type JSON natif. Chaque sch\u00e9ma Oracle qui stocke du JSON n\u00e9cessite le m\u00eame remplacement MODIFY_TYPE et la m\u00eame suppression de contrainte IS JSON. Les r\u00e9\u00e9critures de vues sont un travail manuel unique, mais le <code>STRING_AGG<\/code>, <code>REGROUPEMENT<\/code>et <code>jsonb_array_elements<\/code> les substitutions sont des mod\u00e8les que vous reverrez sur tout sch\u00e9ma Oracle qui poss\u00e8de des vues analytiques ou des vues de requ\u00eates JSON.<\/p>\n\n\n\n<p>La divergence de type de cl\u00e9 \u00e9trang\u00e8re de <code>NOMBRE(38)<\/code> les colonnes sont un probl\u00e8me syst\u00e9matique. <code>PG_INTEGER_TYPE 1<\/code> g\u00e8re nu <code>NOMBRE<\/code> correctement, mais tout ce qui est d\u00e9clar\u00e9 avec pr\u00e9cision \u2014 m\u00eame <code>NOMBRE(38)<\/code>, ce qui est la mani\u00e8re dont Oracle repr\u00e9sente un entier simple dans son dictionnaire de donn\u00e9es \u2014 sort de la r\u00e8gle. Sur un sch\u00e9ma de production, la sortie SHOW_COLUMN contiendra de nombreuses colonnes de ce type. Chacune d'entre elles n\u00e9cessite une entr\u00e9e MODIFY_TYPE avant l'exportation de la table.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<p><em>Si vous pr\u00e9voyez une migration d'Oracle vers PostgreSQL et souhaitez une \u00e9valuation ind\u00e9pendante de la complexit\u00e9, de l'effort et des risques avant de vous engager sur un calendrier, <a href=\"https:\/\/rootfan.com\/fr\/services\/\">Je propose un audit de migration \u00e0 prix fixe<\/a> qui produit exactement cela.<\/em><\/p>","protected":false},"excerpt":{"rendered":"<p>Oracle&#8217;s CO (Customer Orders) schema is the modern replacement for the older OE schema. It ships with Oracle 19c, it is actively maintained, and it is built the way most real Oracle applications are built today: IDENTITY columns instead of sequence-trigger pairs, JSON stored in BLOB columns, and views that use Oracle-specific SQL functions. I &hellip; <\/p>\n<p class=\"link-more\"><a href=\"https:\/\/rootfan.com\/fr\/oracle-to-postgres-migration-issues-and-fixes\/\" class=\"more-link\">Continuer la lecture<span class=\"screen-reader-text\"> de &laquo;&nbsp;Oracle to Postgres Migration: CO Schema Issues and Fixes&nbsp;&raquo;<\/span><\/a><\/p>","protected":false},"author":1,"featured_media":6772,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"rank_math_focus_keyword":"oracle to postgres migration issues","rank_math_title":"","rank_math_description":"Oracle to PostgreSQL migration issues and fixes for the CO schema: IDENTITY sequences, BLOB-to-JSONB conversion, NUMBER(38) FK type mismatches, IS JSON constraint removal, and Oracle view rewrites.","rank_math_robots":"","rank_math_og_title":"","rank_math_og_description":"","jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[146],"tags":[155,143,141,157,156,137,147],"class_list":["post-6754","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oracle-to-postgresql","tag-co-schema","tag-data-migration","tag-data-types","tag-identity-columns","tag-jsonb","tag-migration","tag-ora2pg"],"jetpack_featured_media_url":"https:\/\/i0.wp.com\/rootfan.com\/wp-content\/uploads\/6151061591_1726931f23_b-1.jpg?fit=1024%2C683&ssl=1","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/rootfan.com\/fr\/wp-json\/wp\/v2\/posts\/6754","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/rootfan.com\/fr\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/rootfan.com\/fr\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/rootfan.com\/fr\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/rootfan.com\/fr\/wp-json\/wp\/v2\/comments?post=6754"}],"version-history":[{"count":9,"href":"https:\/\/rootfan.com\/fr\/wp-json\/wp\/v2\/posts\/6754\/revisions"}],"predecessor-version":[{"id":6777,"href":"https:\/\/rootfan.com\/fr\/wp-json\/wp\/v2\/posts\/6754\/revisions\/6777"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/rootfan.com\/fr\/wp-json\/wp\/v2\/media\/6772"}],"wp:attachment":[{"href":"https:\/\/rootfan.com\/fr\/wp-json\/wp\/v2\/media?parent=6754"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/rootfan.com\/fr\/wp-json\/wp\/v2\/categories?post=6754"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/rootfan.com\/fr\/wp-json\/wp\/v2\/tags?post=6754"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}