Formation SQL5
Dalibo SCOP
8 janvier 2018
Vous êtes libres de redistribuer et/ou modifier cette création selon les conditions suivantes :
Paternité
Pas d'utilisation commerciale
Partage des conditions initiales à l'identique
Ce module est organisé en quatre parties :
La première migration est importante :
Le projet doit être choisi avec soin :
Souvent moins important:
Oracle et PostgreSQL sont assez proches :
PostgreSQL et Oracle :
BEGINBEGIN caché)smallint, integer, bigintnumber sous Oracleboolean.boolean sous PostgreSQL alors que vous aurez migré un intvarchar2 dans PostgreSQL
varchar'' = IS NULL
''et NULL sont distinctsvarchar peut ne pas prendre de taille sous PostgreSQL
text équivalent à varchar sans taillelo_*byteaPostgreSQL fournit aussi de nombreux types de données spécialisés :
PERIODip4rYYYY/MM/DD HH:MM:SSYYYY-MM-DD (conforme SQL)YYYY/MM/DD HH:MM:SSHH:MM:SS.mmmmmmm (µs)timestamp sous PostgreSQL : Date+Time (+TZ)YYYY-MM-DD HH24:MI:SS.mmmmmmm+TZIntervalOracle : nombreuses extensions incompatibles avec PostgreSQL :
+)
LEFT JOIN (standard SQL)CONNECT BY
WITH RECURSIVE (standard SQL)COALESCE, CASE (standard SQL)" " autour des identifiants.Oracle et PostgreSQL n'ont pas le même langage PL :
Avant de pouvoir porter l'application et le PL :
number(18,0), number(4,0) …int : -2147483648 à +2147483647 (4 octets, number(9,0))
bigint : -9223372036854775808 à 9223372036854775807 (8 octets, number(18,0))
numeric(x,0), d'autres en int/bigint
Veut-on :
Suivant les réponses aux questions précédentes, vous choisirez :
COPY)blob et clob -> bytea et textnumber -> int, bigint, real, double, decimalLors de la migration, certaines fonctionnalités d'Oracle auront peu ou pas d'équivalent :
Certaines choses peuvent être émulées :
CREATE TABLE AS SELECT régulièrementcreate table sales (year number(4),
                    product varchar2(10),
                   amt number(10,2))
     partition by range (year)
     partition p1 values less than (1992) tablespace u1,
     partition p2 values less than (1993) tablespace u2,
     partition p3 values less than (1994) tablespace u3,
     partition p4 values less than (1995) tablespace u4,
   partition p5 values less than (MAXVALUE) tablespace u5;Pas d'équivalent simple sous PostgreSQL :
CREATE TABLE sales (year numeric(4),
                    product varchar(10),
                   amt numeric(10,2));
CREATE TABLE sales_y1992 (
    CHECK ( year = 1992 )
) INHERITS (sales);
CREATE TABLE sales_y1993 (
    CHECK ( year = 1993 )
) INHERITS (sales);
…Points essentiels :
N'hésitez pas, c'est le moment !
Ce module est organisé en quatre parties :
Étapes :
ora2pg-X.Y.tar.bz2ORACLE_HOME et PATH comprenant sqlplus)libaio1Time::HiResPerl DBI et DBD::OracleEn option :
DBD::Pg pour l'import direct dans PostgreSQLCompress::Zlib : compression des fichiers en sortieDBD::MySQL pour migrer les bases MySQLLe script ora2pg s'utilise de la façon suivante :
ora2pg [-dhpqv --estimate_cost --dump_as_html] [--option value]Utilisation basique :
ora2pg -t ACTION [-c fichier_de_configuration]Étapes de la configuration :
Structure
#)0 pour désactivé et 1 pour activéIMPORT fichier.confORACLE_HOME /path/.../DEBUG [0|1]LOGFILE /path/.../migration.logORACLE_DSN
dbi:Oracle:host=serveur;sid=INSTANCEORACLE_USER
systemORACLE_PWD
managerSCHEMA
NOM_SCHEMA versus SYSUSERSUSER_GRANTS [0|1]
Cette étape de validation de la configuration permet d'obtenir des informations sur la base Oracle :
Certaines informations sont disponibles immédiatement, sans plus de configuration :
SHOW_VERSION affiche la version de l'instance Oracle.SHOW_SCHEMA liste les schémas définis sous Oracle.SHOW_TABLE affiche la liste des tables de la base Oracle.SHOW_COLUMN affiche la liste des colonnes par table d'une base Oracle.Recherche de l'encodage utilisé par l'instance Oracle :
SHOW_ENCODING : affiche les valeurs utilisées par Ora2Pg pour
NLS_LANGCLIENT_ENCODINGNLS_LANG
AMERICAN_AMERICA.AL32UTF8French_France.WE8ISO8895P1...NLS_NCHAR
AL32UTF8...CLIENT_ENCODING
utf8, latin1, latin9BINMODE
utf8, rawRapport exhaustif du contenu de la base Oracle
ora2pg -t SHOW_REPORT
ora2pg -t SHOW_REPORT --dump_as_htmlEstimation du coût de migration
ora2pg -t SHOW_REPORT --estimate_cost
ora2pg -t SHOW_REPORT --estimate_cost --dump_as_htmlLe but du fichier de configuration générique est multiple :
Utilisation de fichiers de sortie dédiés
FILE_PER_CONSTRAINT 1FILE_PER_INDEX 1FILE_PER_TABLE 1FILE_PER_FUNCTION 1Ajout d'ordres SQL :
DISABLE_TRIGGERS 1TRUNCATE_TABLE 1DISABLE_SEQUENCE 1COMPILE_SCHEMA [0|1]Désactivation de la conversion automatique du PL/SQL :
PLSQL_PGSQL 0Utilisation d'un schéma sous PostgreSQL ?
EXPORT_SCHEMA   [0|1]PG_SCHEMA       nom_du_shemaCREATE_SCHEMA   0PG_SUPPORTS_INSTEADOF        1STANDARD_CONFORMING_STRINGS  1PG_SUPPORTS_IFEXISTS         1PG_SUPPORTS_MVIEW            1PG_SUPPORTS_CHECKOPTION      1BITMAP_AS_GIN                1La base contient des champs de type SDO_GEOMETRY.
AUTODETECT_SPATIAL_TYPE [0|1]DEFAULT_SRID   4326CONVERT_SRID   [0|1|N]POSTGIS_SCHEMA schema_nameL'export des champs CLOB et BLOB sont contrôlés par :
LONGREADLEN       8192LONGTRUNCOK          0NO_LOB_LOCATOR       0BLOB_LIMIT         500Étapes :
ora2pg --init_project dirname --project_base dirnameora2pg.conf générique
ora2pg
-t type d'export-b répertoire de stockage des fichiers-o nom du fichier de sortie-p conversion automatique du codeExport des tables, contraintes et index
ora2pg -p -t TABLE -o table.sql -b schema/tables -c config/ora2pg.confExport des séquences
ora2pg -t SEQUENCE -o sequences.sql -b schema/sequences -c  config/ora2pg.confExport des vues
ora2pg -p -t VIEW -o views.sql -b schema/views -c config/ora2pg.confUSE_TABLESPACELes rôles et droits
ora2pg -t GRANT -o users.sql -b schema/users -c config/ora2pg.confLes tablespaces
ora2pg -t TABLESPACE -o tablespaces.sql -b schema/tablespaces \ 
-c config/ora2pg.confLes types composites
ora2pg -p -t TYPE -o types.sql -b schema/types -c config/ora2pg.confora2pg -p -t TRIGGER -o triggers.sql -b schema/triggers -c config/ora2pg.conf
ora2pg -p -t FUNCTION -o functions.sql -b schema/functions -c config/ora2pg.conf
ora2pg -p -t PROCEDURE -o procedures.sql -bschema/procedures -c config/ora2pg.conf
ora2pg -p -t PACKAGE -o packages.sql -b schema/packages -c config/ora2pg.confExtraction du code brut d'Oracle :
ora2pg -t TYPE -o types.sql -b sources/types -c config/ora2pg.conf
ora2pg -t VIEW -o views.sql -b sources/views -c config/ora2pg.conf
ora2pg -t TRIGGER -o triggers.sql -b sources/triggers -c config/ora2pg.conf
ora2pg -t FUNCTION -o functions.sql -b sources/functions -c config/ora2pg.conf
ora2pg -t PROCEDURE -o procedures.sql -b sources/procedures -c config/ora2pg.conf
ora2pg -t PACKAGE -o packages.sql -b sources/packages -c config/ora2pg.confExport des partitions :
ora2pg -t PARTITION -o partitions.sql -b schema/partitions -c config/ora2pg.confDepuis PostgreSQL 9.3 :
PG_SUPPORT_MVIEW          1
ora2pg -t MVIEW -o mviews.sql -b schema/mviews -c config/ora2pg.confAvant la 9.3 :
PostgreSQL ne possède pas d'objet de type SYNONYM :
search_pathora2pg -t SYNONYM -o synonyms.sql -b schema/synonyms -c config/ora2pg.confPostgreSQL ne possède pas d'objets de type DIRECTORY ni de tables EXTERNAL.
file_fdw.
COPYora2pg -t DIRECTORY -o directories.sql -b schema/directories -c config/ora2pg.confPostgreSQL ne possède pas d'objets de type DATABASE LINK.
oracle_fdw.
ora2pg -t DBLINK -o dblinks.sql -b schema/dblinks -c config/ora2pg.confDIRECTORY et BFILE
BFILE en donnée bytea par défautBFILE est redéfini en TEXT, stockage du chemin du fichier externeexternal_file.
EFILE correspondant au type BFILE : (directory_name, filename)BFILE est redéfini en EFILE (directive DATA_TYPE)Oracle Index Texte
CONTAINSCATSEARCHPostgreSQL : Full Text Search/Recherche Plein Texte
opérateur @@ équivalent à CONTAINS
SELECT to_tsvector('fat cats ate fat rats') @@ to_tsquery('fat & rat');pg_trgm pour les recherches LIKE '%mot%mot%', équivalent de CTXCATEXPORT_SCHEMA est activé
Création des objets du schéma :
psql -U myuser -f schema/tables/tables.sql mydb >> create_mydb.log 2>&1
psql -U myuser -f schema/partitions/partitions.sql mydb >> create_mydb.log 2>&1
psql -U myuser -f schema/views/views.sql mydb >> create_mydb.log 2>&1
psql -U myuser -f schema/tablespaces/tablespaces.sql mydb >> create_mydb.log 2>&1Chargements différé de certains objets :
Bilan de l'export/import du schéma :
CHECK et conditions des indexCHECK (WYEAR between 0 and 9);CHECK (wyear::integer between 0 and 9);Étapes :
Création des fichiers de données :
ora2pg -t COPY -o datas.sql -b data/ -c config/ora2pg.conf
ora2pg -t INSERT -o datas.sql -b data/ -c config/ora2pg.confFILE_PER_TABLE    1bytea
BLOB et CLOB en bytea très lentLOBSRID, système spatial de référence
CONVERT_SRID converti la valeur Oracle dans la norme EPSGDEFAULT_SRID force la valeur du SRID par défautGEOMETRY_EXTRACT_TYPE [WKT|WKB|INTERNAL]Import des fichiers de données :
psql -U myuser -f data/datas.sql mydb >> data_mydb.log 2>&1
gunzip -c data/datas.sql.gz | psql  -U myuser  mydb >> data_mydb.log 2>&1
bunzip2 -c data/datas.sql.bz2 | psql  -U myuser  mydb >> data_mydb.log 2>&1Restauration des contraintes, triggers, séquences et index
psql -U myuser -f schema/tables/CONSTRAINTS_tables.sql mygdb >> create_mydb.log 2>&1
psql -U myuser -f schema/tables/INDEXES_tables.sql mygdb >> create_mydb.log 2>&1
psql -U myuser -f schema/sequences/sequences.sql mygdb >> create_mydb.log 2>&1
psql -U myuser -f schema/triggers/triggers.sql mygdb >> create_mydb.log 2>&1Action :
LOAD permet de paralléliser des ordres SQL sur N processusora2pg -c config/ora2pg -t LOAD -j 4 -i schema/tables/INDEXES_tables.sqlora2pg -c config/ora2pg -t LOAD -j 4 -i schema/tables/CONSTRAINTS_tables.sqlCOPYCLIENT_ENCODINGDEFAULT_NUMERIC ou ALTER TABLECLOB, BLOB et XML : LONGREADLENCOPYImport direct dans PostgreSQL
PG_DSN dbi:Pg:dbname=test_db;host=localhost;port=5432
PG_USER  [nom_utilisateur]
PG_PWD [mot_de_passe]DATA_LIMITJOBS    NcoresORACLE_COPIES     NcoresDEFINED_PKEY      EMPLOYEE:IDPARALLEL_TABLES    NcoresJOBS x ORACLE_COPIES | PARALLEL_TABLES = Total Nombre cœursALLOW TABLE1 TABLE2 [..] TABLENEXCLUDE TABLE1 TABLE2 [..] TABLENWHERE TABLE[condition valide] GLOBAL_CONDITIONWHERE TABLE_TEST[ID1='001']WHERE DATE_CREATION > '2001-01-01'REPLACE_QUERY  TABLENAME[SQL_QUERY]N'hésitez pas, c'est le moment !
Ce module est organisé en cinq parties :
to_char(1 param), add_month(), decode()...DBMS_ALERT, DBMS_PIPE, DBMS_OUTPUT, DBMS_RANDOM et UTL_FILEfillfactorDBLINK) n'existent pas sauf sous forme d'extension (dblink ou fdw).Opérations sur les dates :
DATE + NUMBER
DATE + interval '1 jours'TIMESTAMP - TIMESTAMP = NUMBER
intervalNLS_DATE_FORMAT (TO_CHAR et TO_DATE)
DateStylePas de conversion implicite vers et depuis les types chaînes de caractères :
SELECT * FROM depts WHERE numero BETWEEN 0 AND 42;REVERSE LOOP => inversion des bornesCONNECT BY n'existe pas, utiliser WITH RECURSIVEREF CURSOR doit être remplacé par REFCURSORnom_curseur%ROWTYPE doit être remplacé par RECORDBULK COLLECT => Array:NEW et :OLD => NEW et OLDUPDATING, INSERTING, DELETING => TG_OP (UPDATE, INSERT, DELETE)RETURN NEW impératif dans les triggers BEFORE, retour implicite sous OraclecustomLeft outer join :
SELECT * FROM a, b WHERE a.id = b.id (+)
=> SELECT * FROM a LEFT OUTER JOIN b ON (a.id = b.id)Right outer join :
SELECT * FROM  a, c WHERE a.id (+) = c.id
=> SELECT * FROM a RIGHT OUTER JOIN c ON (a.id = c.id)Full outer join :
SELECT * FROM a, b WHERE a.id = b.id (+)
UNION ALL SELECT * FROM a, b WHERE a.id (+) = b.id AND a.id = NULL
=> SELECT * FROM a FULL OUTER JOIN b ON (a.id = b.id)PACKAGES ou paquets de procédures stockées.Les noms diffèrent :
NVL() => coalesce()SYSDATE => LOCALTIMESTAMP
CURRENT_TIMESTAMP sans le fuseau horaireLes paramètres changent :
to_number(num)
to_number(num, '99...99D99...99')to_date( string1, format_mask,  nls_language)
to_date(text, text)replace(a, b)
replace(a, b, ' ')Les noms et les paramètres changent :
trunc(.*date.*)
 => date_trunc('day', ...date...)substr( string, start_position, length )
 => substring(string from start_position for length)La réécriture est complète :
add_months
 => ”+ 'N months'::interval”add_years
 => ”+ 'N year'::interval”TO_NUMBER(TO_CHAR(…]
 => to_char(…)::integerdecode("user_status",'active',"username",null)
 => (CASE WHEN user_status='active' THEN username ELSE NULL END)(nom.nextval => nextval('nom')nom.currval => currval('nom']:new. en NEW. et :old. en OLD. dans les triggersINSERTING|DELETING|UPDATING en TG_OP = 'INSERT|DELETE|UPDATE' dans les fonction de triggerDBMS_OUTPUT.(put_line|put|new_line)(...) en RAISE NOTICE '...'FOR … IN … REVERSE min .. maxsprintf utilisé par PostgreSQLROWNUM dans la clause where par des clauses LIMIT et/ou OFFSETHAVING … GROUP BY (variante acceptée par Oracle mais pas PostgreSQL) en GROUP BY … HAVINGSTRICT aux SELECT … INTO lorsqu'il y a EXCEPTION … NO_DATA_FOUND ou TOO_MANY_ROWSMINUS par EXCEPTFROM DUALDEFAULT NULL qui est la valeur par défaut sous PostgreSQL lorsqu'aucune valeur par défaut n'est préciséeEND fct_name; est réécrit en END;CASE entre le WHEN et le THEN, non supporté par PostgreSQLIS NULL et IS NOT NULL par des instructions à base de coalesce (pour Oracle, une chaîne vide est équivalente à NULL)CURSOR moncurseur; pour les rendre compatibles avec PostgreSQL : moncurseur CURSOR;IN de la déclaration des curseurs.EXIT WHEN ...%NOTFOUND par IF NOT FOUND THEN EXIT; END IF;STRICT dans les requêtes SELECT ... INTO ... si une exception sur NO_DATA_FOUND ou TOO_MANY_ROW est levéeREGEX_LIKE( string, pattern ) en syntaxe avec l'opérateur PostgreSQL de recherche regex string ~ pattern.SYS_CONTECT('USERENV', ...) en équivalent PostgreSQL.SDO_GEOM.* en appels aux fonction PostGis équivalentes.SDO_* en opérateurs correspondants PostGis.Remplacement de :
STORAGE_ERROR par OUT_OF_MEMORYZERO_DIVIDE par DIVISION_BY_ZEROINVALID_CURSOR par INVALID_CURSOR_STATESQLCODE par le presque équivalent SQLSTATE sous PostgreSQLraise_application_error en RAISE EXCEPTIONRemplacement de :
SYS_REFCURSOR par REFCURSORSQL%NOTFOUND par NOT FOUNDSYS_EXTRACT_UTC par AT TIME ZONE 'UTC'dup_val_on_index en unique_violationÉtapes :
Non supportées nativement par PostgreSQL, Ora2Pg utilise une fonction de substitution :
_atx_atx au travers d'un dblink.Chargement des fonctions et procédures :
psql --single-transaction -U myuser -f schema/procedures/procedures.sql mydb
psql --single-transaction -U myuser -f schema/functions/functions.sql mydbChargement des paquets de fonctions :
psql --single-transaction -U myuser -f schema/packages/packages.sql mydbAbsence de certaines fonctions ou paquets de fonctions dans l'export
COMPILE_SCHEMAEXPORT_INVALIDCertains commentaires des paquets de fonctions ne sont pas importés
Valider le portage du code :
N'hésitez pas, c'est le moment !
Table DUAL pas nécessaire
SELECT fonction();SELECT current_timestamp;SELECT 1 = 'a'::text;''  = IS NULL
'' et NULL sont distinctslarge objects et fonctions lo_*byteaPostgreSQL fournit aussi de nombreux types de données spécialisés :
SYSDATEYYYY/MM/DD HH:MM:SSYYYY-MM-DD (conforme SQL)YYYY/MM/DD HH:MM:SSHH:MM:SS.mmmmmmm (µs)Format de sortie conforme SQL sous PostgreSQL:
YYYY-MM-DD HH24:MI:SS.mmmmmmm+TZSYSDATE
équivalent direct :
SELECT localtimestamp;current_timestampcurrent_datecurrent_timePostgreSQL ne propose pas de fonctions add_months, etc.
SELECT current_date + interval '3 days';SELECT current_date + interval '1 days' * 3;SELECT (now() - '2014-01-01') * 2 + now()Équivalent de la clause CASE du standard
CASE expr
  WHEN valeur1 THEN valeur_retour1
  WHEN valeur2 THEN valeur_retour2
  ELSE valeur_retour3
ENDCASE
  WHEN expr1 THEN valeur_retour1
  WHEN expr2 THEN valeur_retour2
  ELSE valeur_retour3
ENDRetourne le premier argument non NULL
SELECT NVL(description, description_courte, '(aucune)')FROM articles;Équivalent de la norme SQL : COALESCE
SELECT COALESCE(description, description_courte, '(aucune)') FROM articles;WITH RECURSIVE obligatoire dans PostgreSQLROWNUM n'existe pas dans PostgreSQL
row_number() OVER ()ORDER BYWHERE ROWNUM < 11LIMIT xx :SELECT *
  FROM employees
 LIMIT 10;FROM tab1, tab2 WHERE tab1.col = tab2.colFROM tab1 JOIN tab2 ON (tab1.col = tab2.colLEFT JOINRIGHT JOINFULL OUTER JOINFROM t1, t2;FROM t1 CROSS JOIN t2GROUP BY après HAVINGGROUP BY avant HAVINGUNION / UNION ALLINTERSECTEXCEPT
MINUSBEGINBEGIN caché)BEGIN TRANSACTION ISOLATION LEVEL xxxx
READ COMMITTEDREPEATABLE READSERIALIZABLESAVEPOINTRELEASE SAVEPOINTROLLBACK TO SAVEPOINTSELECTSELECT FOR SHARE/UPDATE
LOCK TABLECONNECT BY OracleWITH RECURSIVE PostgreSQLSTART WITH
CONNECT BY PRIOR
SELECT empno, ename, job, mgr
  FROM emp
  START WITH mgr IS NULL
  CONNECT BY PRIOR empno = mgrWITH RECURSIVE hierarchie AS (
condition de départ
UNION ALL
clause de récursion
)
SELECT * FROM hierarchieLEVEL donne le niveau de hiérarchiecondition de départ
1 AS levelclause de récursion
prior.level + 1niveau 1/niveau 2/niveau 3niveau initial AS pathprior.path || niveau courantNOCYCLEcycleelement = ANY (tableau) AS cycleWHERE cycle = false