Skip to content
Snippets Groups Projects
Select Git revision
  • patch-1
  • zf-3.x
  • master default protected
  • Fix
  • origin/trunk
  • origin/new_version
  • 1.1.1
  • 1.1.0
  • 1.0.0
9 results

db-import

  • Clone with SSH
  • Clone with HTTPS
  • Forked from lib / unicaen / db-import
    225 commits behind the upstream repository.

    UnicaenDbImport

    Ce module a pour but de simplifier l'import de données d'une table (ou d'un select) d'une base de données source vers une table d'une base de données destination.

    Concrètement :

    • Si la source contient un enregistrement qui n'existe pas dans la destination, il est ajouté dans cette dernière.
    • Si la source contient un enregistrement qui existe aussi dans la destination avec les mêmes valeurs de colonnes, rien n'est fait.
    • Si la source contient un enregistrement qui existe aussi dans la destination mais avec des valeurs de colonnes différentes, les colonnes de la destination sont mises à jour en conséquence.
    • Si la source ne contient pas un enregistrement qui existe dans la destination, l'enregistrement destination est historisé (i.e. marqué "supprimé").
    • Si la source contient un enregistrement qui existe aussi dans la destination mais marqué "supprimé", ce dernier est dé-historisé.

    Les enregistrements source et les enregistrements destination doivent avoir un identifiant unique en commun permettant de les rapprocher : on l'appellera "code source" (cf. paramètre de config source_code_column). Cet identifiant doit être de type chaîne de caractères.

    Les données sources sont recopiées dans une table intermédiaire et c'est à partir de cette table intermédiaire qu'est réalisé le différentiel avec la table destination.

    La différence avec le module UnicaenImport ?

    Comme son nom de l'indique pas, UnicaenImport fonctionne uniquement entre 2 bases de données Oracle ; et la synchronisation est faite entièrement par le SGBD (ou presque).

    UnicaenDbImport fonctionne en majeure partie en PHP tout en déléguant au maximum au SGBD ce qu'il sait bien faire (select FULL OUTER JOIN pour le différentiel entre données source et destination, fonction pour l'alimentation du registre d'import, etc.) Par conséquent, il est possible et souhaitable de l'enrichir pour implémenter une synchronisation vers différentes plateformes de base de données destination. Les plateformes de base de données destination suivantes sont implémentées :

    • PostgreSQL

    NB: En revanche toutes les plateformes de base de données source sont supportées car on se contente d'y faire un select à l'aide de l'ORM Doctrine 2 pour alimenter une table intermédiaire.

    Installation

    composer require unicaen/unicaen-db-import

    Configuration

    • Récupérer les fichiers config de distribution :
    cp -n vendor/unicaen/db-import/config/unicaen-db-import.global.php.dist config/autoload/unicaen-db-import.global.php
    cp -n vendor/unicaen/db-import/config/unicaen-db-import.local.php.dist config/autoload/unicaen-db-import.local.php
    • Adapter leur contenu à vos besoins.

    Utilisation

    Le module fournit une ligne de commande pour :

    • lancer un import par son nom, exemple :

      php public/index.php run import --name "Import_PFI_SIFAC"
    • lancer tous les imports :

      php public/index.php run import --all

    NB: Cette commande exécute la synchronisation une fois. Pour synchroniser en permanence la base de données destination, il faut programmer le lancement périodique de cette commande à l'aide de CRON (cf. plus bas pour un exemple).

    Contraintes

    • L'identifiant unique commun des enregistrements source et destination doit être de type chaîne de caractères.

    • Les colonnes synchronisables de la table destination ne peuvent être que de type chaîne de caractères (varchar par exemple). Donc 2 solutions :

      • Dans le cas d'une source de données spécifiée par un "select", convertir en amont chaque colonne en chaîne de caractères (en faisant un TO_CHAR(debut_validite,'YYYY-MM-DD') par exemple en PostgreSQL) ;

      • Dans le cas d'une source de type "table", spécifier dans la config de la destination la fonction de conversion à appliquer à chaque colonne pour la convertir en chaîne de caractères (cf. paramètre de config destination columns_to_char).

    • La table destination doit posséder les colonnes d'historique suivantes (syntaxe PostgreSQL) :

    created_on TIMESTAMP(0) WITH TIME ZONE DEFAULT LOCALTIMESTAMP(0) NOT NULL
    updated_on TIMESTAMP(0) WITH TIME ZONE
    deleted_on TIMESTAMP(0) WITH TIME ZONE

    Exemple de mise en oeuvre

    Import des programmes de financement SIFAC depuis une base Oracle vers une table PostgreSQL. Les données sources sont "extraites" grâce à un select.

    • Configuration globale du module :

    unicaen-db-import.global.php

    return [
        /**
         * Configuration du module UnicaenDbImport.
         */
        'import' => [
            /**
             * Liste des imports.
             */
            'imports' => [
                [
                    /**
                     * Petit nom (unique) de l'import.
                     */
                    'name' => 'Import_PFI_SIFAC',
     
                    /**
                     * Configuration de la source de données à importer :
                     * - 'name'               : petit nom (unique) de la source
                     * - 'table'              : nom de la table source contenant les données à importer
                     * - 'select'             : select SQL de mise en forme des données source à importer (NB: antinomique avec 'table')
                     * - 'connection'         : identifiant de la connexion Doctrine à la base de données source
                     * - 'source_code_column' : nom de la colonne dans la table/vue source contenant l'identifiant unique
                     * - 'columns'            : liste ordonnée des noms des colonnes à prendre en compte dans la table/vue source
                     */
                    'source' => [
                        'name'               => 'PFI SIFAC',
                        'connection'         => 'doctrine.connection.orm_oracle',
                        'source_code_column' => 'CODE',
                        'columns'            => ['LIBELLE', 'FLECHE', 'DEBUT_VALIDITE', 'FIN_VALIDITE'],
                        'select'             => <<<'EOT'
    select distinct
      MEASURE                                               CODE,
      SHORT_DESC                                            LIBELLE,
      ZFLECHE                                               FLECHE,
      to_char(TO_DATE(VALID_FROM,'YYYYMMDD'),'YYYY-MM-DD')  DEBUT_VALIDITE,
      to_char(TO_DATE(VALID_TO,'YYYYMMDD'),'YYYY-MM-DD')    FIN_VALIDITE
      from (
        SELECT
          SAPSR3.FMMEASURE.MEASURE,
          SAPSR3.FMMEASURET.SHORT_DESC,
          SAPSR3.FMMEASURE.ZFLECHE,
          SAPSR3.FMMEASURE.VALID_FROM,
          SAPSR3.FMMEASURE.VALID_TO
        FROM
          SAPSR3.FMMEASURE,
          SAPSR3.FMMEASURET
        WHERE
          ( (SAPSR3.FMMEASURE.CLIENT in  (select A.MANDT from SAPSR3.FM01H A where A.MANDT = '500') OR SAPSR3.FMMEASURE.CLIENT is null)
        and (SAPSR3.FMMEASURE.FMAREA in  (select A.FIKRS from SAPSR3.FM01H A where A.FIKRS = '1010')  or SAPSR3.FMMEASURE.FMAREA is null)  )
          AND  ( SAPSR3.FMMEASURE.CLIENT=SAPSR3.FMMEASURET.CLIENT(+)  )
          AND  ( SAPSR3.FMMEASURE.FMAREA=SAPSR3.FMMEASURET.FMAREA(+)  )
          AND  ( SAPSR3.FMMEASURE.MEASURE=SAPSR3.FMMEASURET.MEASURE(+)  )
    )
    EOT
                        ,
                    ],
     
                    /**
                     * Forçage éventuel du nom de la table intermédiaire utilisée lorsque source et destination
                     * ne partagent pas la même connexion. NB: cette table intermédiaire est créée/peuplée/supprimée
                     * dans la base de données destination à chaque import.
                     * En l'absence de ce forçage, le nom de la table intermédiaire sera celui de la table destination
                     * préfixé par "src_".
                     */
                    'intermediate_table' => 'src_progfin',
     
                    /**
                     * Configuration de la destination des données importées :
                     * - 'name'               : petit nom (unique) de la destination
                     * - 'table'              : nom de la table destination vers laquelle les données sont importées
                     * - 'connection'         : identifiant de la connexion Doctrine à la base de données destination
                     * - 'source_code_column' : nom de la colonne dans la table destination contenant l'identifiant unique
                     * - 'columns'            : liste ordonnée des noms des colonnes importées dans la table destination
                     * - 'columns_to_char'    : format sprintf nécessaire pour mettre des colonnes au format requis (string)
                     */
                    'destination' => [
                        'name'               => 'PFI Zébu',
                        'table'              => 'progfin',
                        'connection'         => 'doctrine.connection.orm_default',
                        'source_code_column' => 'code',
                        'columns'            => ['libelle', 'fleche', 'debut_validite', 'fin_validite'],
                        'columns_to_char' => [
                            'debut_validite' => "TO_CHAR(%s,'YYYY-MM-DD')", // car colonne destination de type TIMESTAMP
                            'fin_validite'   => "TO_CHAR(%s,'YYYY-MM-DD')", // idem
                        ],
                    ],
                ],
            ],
        ],
    ];

    On doit renseigner le paramètre intermediate_table car les données source proviennent d'un select.

    • Configuration locale du module :

    unicaen-db-import.local.php

    return [
     /**
      * Configuration Doctrine minimum requise.
      */
     'doctrine' => [
         'connection' => [
             'orm_oracle' => [
                 'driverClass' => 'Doctrine\\DBAL\\Driver\\OCI8\\Driver',
                 'params'      => [
                     'host'     => 'host.domain.fr',
                     'port'     => '1525',
                     'user'     => 'x',
                     'password' => 'y',
                     'dbname'   => 'z',
                     'charset'  => 'AL32UTF8',
                 ]
             ],
             'orm_default' => [
                 'driverClass' => 'Doctrine\\DBAL\\Driver\\PDOPgSql\\Driver',
                 'params' => [
                     'host'     => 'host.domain.fr',
                     'port'     => '5432',
                     'charset'  => 'utf8',
                     'dbname'   => 'a',
                     'user'     => 'b',
                     'password' => 'c',
    
                 ],
             ],
         ],
     ],
    ];

    NB: les paramètres de config de la connexion orm_default existent sans doute déjà dans la configuration de votre application, il est bien-sûr inutile de les répéter dans la config du module unicaen/db-import.

    • Configuration CRON pour lancer périodiquement la synchro :
    # Du lundi au vendredi, entre 6h00 et 19h45, toutes les 15 minutes
    */15 6-19 * * 1-5   root    /usr/bin/php /var/www/zebu-back/public/index.php run import --all 1> /tmp/zebu-cron.log 2>&1
    • Exemple de logs affichés par la commande :
    ######################## IMPORTS ########################
    
    ### Import des PFI SIFAC ###
    05/09/2017 16:22:08
    # delete :
    UPDATE progfin SET deleted_on = LOCALTIMESTAMP(0) WHERE CODE = 'fddsfsd' ; UPDATE import_reg SET executed_on = LOCALTIMESTAMP(0) WHERE import_hash = 'fddsfsd-99c4b652-9245-11e7-bddd-0242f725575b' ;
    (1 instructions exécutées)
    # insert :
    INSERT INTO progfin(CODE, LIBELLE, FLECHE, DEBUT_VALIDITE, FIN_VALIDITE, created_on) VALUES ('971UP009', 'Of Wisco', ' ', '2014-01-01', '2047-12-31', LOCALTIMESTAMP(0)) ; UPDATE import_reg SET executed_on = LOCALTIMESTAMP(0) WHERE import_hash = '971UP009-99c4b652-9245-11e7-bddd-0242f725575b' ;
    INSERT INTO progfin(CODE, LIBELLE, FLECHE, DEBUT_VALIDITE, FIN_VALIDITE, created_on) VALUES ('977CA025', 'Screening de 5', ' ', '2016-07-21', '2018-12-31', LOCALTIMESTAMP(0)) ; UPDATE import_reg SET executed_on = LOCALTIMESTAMP(0) WHERE import_hash = '977CA025-99c4b652-9245-11e7-bddd-0242f725575b' ;
    INSERT INTO progfin(CODE, LIBELLE, FLECHE, DEBUT_VALIDITE, FIN_VALIDITE, created_on) VALUES ('F950MEC1', 'MDE CAEN FONCTIONNEM', ' ', '2015-01-01', '2047-12-31', LOCALTIMESTAMP(0)) ; UPDATE import_reg SET executed_on = LOCALTIMESTAMP(0) WHERE import_hash = 'F950MEC1-99c4b652-9245-11e7-bddd-0242f725575b' ;
    INSERT INTO progfin(CODE, LIBELLE, FLECHE, DEBUT_VALIDITE, FIN_VALIDITE, created_on) VALUES ('F971CF03', 'UNIVERSITE DU HAVRE', ' ', '2015-01-01', '2047-12-31', LOCALTIMESTAMP(0)) ; UPDATE import_reg SET executed_on = LOCALTIMESTAMP(0) WHERE import_hash = 'F971CF03-99c4b652-9245-11e7-bddd-0242f725575b' ;
    INSERT INTO progfin(CODE, LIBELLE, FLECHE, DEBUT_VALIDITE, FIN_VALIDITE, created_on) VALUES ('014DU009', 'DU RDCM', ' ', '2016-01-01', '2047-12-31', LOCALTIMESTAMP(0)) ; UPDATE import_reg SET executed_on = LOCALTIMESTAMP(0) WHERE import_hash = '014DU009-99c4b652-9245-11e7-bddd-0242f725575b' ;
    INSERT INTO progfin(CODE, LIBELLE, FLECHE, DEBUT_VALIDITE, FIN_VALIDITE, created_on) VALUES ('012RE001', 'COLLOQUE 50 ANS SC E', ' ', '2017-01-01', '2017-12-31', LOCALTIMESTAMP(0)) ; UPDATE import_reg SET executed_on = LOCALTIMESTAMP(0) WHERE import_hash = '012RE001-99c4b652-9245-11e7-bddd-0242f725575b' ;
    INSERT INTO progfin(CODE, LIBELLE, FLECHE, DEBUT_VALIDITE, FIN_VALIDITE, created_on) VALUES ('012CH021', 'Typo-chronologie', ' ', '2017-01-01', '2017-12-31', LOCALTIMESTAMP(0)) ; UPDATE import_reg SET executed_on = LOCALTIMESTAMP(0) WHERE import_hash = '012CH021-99c4b652-9245-11e7-bddd-0242f725575b' ;
    INSERT INTO progfin(CODE, LIBELLE, FLECHE, DEBUT_VALIDITE, FIN_VALIDITE, created_on) VALUES ('013PL002', 'CMABIO', ' ', '2016-01-01', '2047-12-31', LOCALTIMESTAMP(0)) ; UPDATE import_reg SET executed_on = LOCALTIMESTAMP(0) WHERE import_hash = '013PL002-99c4b652-9245-11e7-bddd-0242f725575b' ;
    INSERT INTO progfin(CODE, LIBELLE, FLECHE, DEBUT_VALIDITE, FIN_VALIDITE, created_on) VALUES ('999CM003', '2016-AGRI-133', ' ', '2016-03-30', '2018-12-31', LOCALTIMESTAMP(0)) ; UPDATE import_reg SET executed_on = LOCALTIMESTAMP(0) WHERE import_hash = '999CM003-99c4b652-9245-11e7-bddd-0242f725575b' ;
    (9 instructions exécutées)
    # undelete :
    UPDATE progfin SET LIBELLE = 'TA 2017', FLECHE = ' ', DEBUT_VALIDITE = '2017-01-01', FIN_VALIDITE = '2047-12-31', updated_on = LOCALTIMESTAMP(0), deleted_on = null WHERE CODE = '913TA017' ; UPDATE import_reg SET executed_on = LOCALTIMESTAMP(0) WHERE import_hash = '913TA017-99c4b652-9245-11e7-bddd-0242f725575b' ;
    UPDATE progfin SET LIBELLE = 'TA 2017', FLECHE = ' ', DEBUT_VALIDITE = '2017-01-01', FIN_VALIDITE = '2047-12-31', updated_on = LOCALTIMESTAMP(0), deleted_on = null WHERE CODE = '920TA017' ; UPDATE import_reg SET executed_on = LOCALTIMESTAMP(0) WHERE import_hash = '920TA017-99c4b652-9245-11e7-bddd-0242f725575b' ;
    UPDATE progfin SET LIBELLE = 'PAIE DIU ADOLESCENTS', FLECHE = ' ', DEBUT_VALIDITE = '2017-01-01', FIN_VALIDITE = '2047-12-31', updated_on = LOCALTIMESTAMP(0), deleted_on = null WHERE CODE = 'P14DU910' ; UPDATE import_reg SET executed_on = LOCALTIMESTAMP(0) WHERE import_hash = 'P14DU910-99c4b652-9245-11e7-bddd-0242f725575b' ;
    (3 instructions exécutées)
    # update :
    UPDATE progfin SET LIBELLE = 'PLATIN''', updated_on = LOCALTIMESTAMP(0) WHERE CODE = '925PL005' ; UPDATE import_reg SET executed_on = LOCALTIMESTAMP(0) WHERE import_hash = '925PL005-PLATINvxcvxc-99c4b652-9245-11e7-bddd-0242f725575b' ;
    (1 instructions exécutées)
    
    Import réalisé avec succès.

    Dans le moteur

    Table intermédiaire SRC_*

    Lorsque les données source sont issues d'un select, une table intermédiaire SRC_* est créée dans la base de données destination à partir de ces données source. La synchronisation est ensuite réalisée entre cette table intermdiaire et la table destination. À la fin du processus de synchronisation, cette table est supprimée.

    NB: attention, si au lancement de la commande d'import la table intermédiaire SRC_* existe déjà dans la base de données, l'import échouera.

    Différentiel entre source et destination

    La requête utilisée pour comparer les données sources et destination pour une table destination ztemptable ressemble à cela (syntaxe PostgreSQL) :

    SELECT create_import_metarequest_for_ztemptable(
        src.code, src.libelle, TO_CHAR(src.debut_validite,'YYYY-MM-DD'),
        dest.code, dest.libelle, TO_CHAR(dest.debut_validite,'YYYY-MM-DD'), dest.deleted_on,
        'eb1ab85c-916a-11e7-aba7-0242f725575b'
    ) AS operation
    FROM src_ztemptable src
    FULL OUTER JOIN ztemptable dest ON src.code = dest.code
    ;

    Fonction create_import_metarequest_for_*

    Le module crée automatiquement dans le SGBD pour chaque import configuré une fonction create_import_metarequest_for_* chargée de :

    • déterminer l'opération de mise à jour nécessaire à partir du résultat du différentiel entre données source et destination ;
    • inscrire dans un "registre d'import" (table IMPORT_REG) les instructions SQL nécessaires à la mise à jour de la table destination.

    Voici à quoi ressemble cette fonction pour une table destination ztemptable (syntaxe PostgreSQL) :

    CREATE OR REPLACE FUNCTION create_import_metarequest_for_ztemptable(
        src_code TEXT, src_libelle TEXT, src_debut_validite TEXT, src_fin_validite TEXT,
        dest_code TEXT, dest_libelle TEXT, dest_debut_validite TEXT, dest_fin_validite TEXT, dest_deleted_on TIMESTAMP(0) WITH TIME ZONE,
        import_hash VARCHAR(255)
    ) RETURNS VARCHAR(255) AS
    $Q$
    DECLARE
        operation VARCHAR(64);
        hash VARCHAR(255);
        SQL TEXT;
    BEGIN
        -- l'enregistrement existe dans la source mais pas dans la destination : il devra être ajouté
        IF (src_code IS NOT NULL AND dest_code IS NULL) THEN
            operation = 'insert';
            hash = concat(concat(src_code, '-'), import_hash);
            SQL = concat('INSERT INTO ztemptable(code, libelle, debut_validite, fin_validite, created_on) VALUES (', quote_literal(src_code), ', ', quote_literal(src_libelle), ', ', quote_literal(src_debut_validite), ', ', quote_literal(src_fin_validite), ', LOCALTIMESTAMP(0)) ;');
            SQL = concat(SQL, ' UPDATE import_reg SET executed_on = LOCALTIMESTAMP(0) WHERE import_hash = ''', hash, ''' ;');
            INSERT INTO import_reg(operation, TABLE_NAME, source_code, field_name, to_value, from_value, SQL, created_on, import_hash) VALUES ('insert', 'ztemptable', src_code, NULL, NULL, NULL, SQL, LOCALTIMESTAMP(0), hash);
        END IF;
     
        -- l'enregistrement existe dans la destination et n'est pas historisé
        IF (src_code IS NOT NULL AND dest_code IS NOT NULL AND dest_deleted_on IS NULL) THEN
            -- 'libelle' doit être mis à jour
            IF (src_libelle <> dest_libelle) THEN
                operation = 'update';
                hash = concat(concat(concat(concat(dest_code, '-'), dest_libelle), '-'), import_hash);
                SQL = concat('UPDATE ztemptable SET libelle = ', quote_literal(src_libelle), ', updated_on = LOCALTIMESTAMP(0) WHERE code = ', quote_literal(dest_code), ' ;');
                SQL = concat(SQL, ' UPDATE import_reg SET executed_on = LOCALTIMESTAMP(0) WHERE import_hash = ''', hash, ''' ;');
                INSERT INTO import_reg(operation, TABLE_NAME, source_code, field_name, to_value, from_value, SQL, created_on, import_hash) VALUES ('update', 'ztemptable', src_code, 'libelle', src_libelle, dest_libelle, SQL, LOCALTIMESTAMP(0), hash);
            END IF;
            -- 'debut_validite' doit être mis à jour
            IF (src_debut_validite <> dest_debut_validite) THEN
                operation = 'update';
                hash = concat(concat(concat(concat(dest_code, '-'), dest_debut_validite), '-'), import_hash);
                SQL = concat('UPDATE ztemptable SET debut_validite = ', quote_literal(src_debut_validite), ', updated_on = LOCALTIMESTAMP(0) WHERE code = ', quote_literal(dest_code), ' ;');
                SQL = concat(SQL, ' UPDATE import_reg SET executed_on = LOCALTIMESTAMP(0) WHERE import_hash = ''', hash, ''' ;');
                INSERT INTO import_reg(operation, TABLE_NAME, source_code, field_name, to_value, from_value, SQL, created_on, import_hash) VALUES ('update', 'ztemptable', src_code, 'debut_validite', src_debut_validite, dest_debut_validite, SQL, LOCALTIMESTAMP(0), hash);
            END IF;
            -- 'fin_validite' doit être mis à jour
            IF (src_fin_validite <> dest_fin_validite) THEN
                operation = 'update';
                hash = concat(concat(concat(concat(dest_code, '-'), dest_fin_validite), '-'), import_hash);
                SQL = concat('UPDATE ztemptable SET fin_validite = ', quote_literal(src_fin_validite), ', updated_on = LOCALTIMESTAMP(0) WHERE code = ', quote_literal(dest_code), ' ;');
                SQL = concat(SQL, ' UPDATE import_reg SET executed_on = LOCALTIMESTAMP(0) WHERE import_hash = ''', hash, ''' ;');
                INSERT INTO import_reg(operation, TABLE_NAME, source_code, field_name, to_value, from_value, SQL, created_on, import_hash) VALUES ('update', 'ztemptable', src_code, 'fin_validite', src_fin_validite, dest_fin_validite, SQL, LOCALTIMESTAMP(0), hash);
            END IF;
     
        END IF;
     
        -- l'enregistrement existe dans la destination mais historisé : il sera dé-historisé
        IF (src_code IS NOT NULL AND dest_code IS NOT NULL AND dest_deleted_on IS NOT NULL) THEN
            operation = 'undelete';
            hash = concat(concat(dest_code, '-'), import_hash);
            SQL = concat('UPDATE ztemptable SET libelle = ', quote_literal(src_libelle), ', debut_validite = ', quote_literal(src_debut_validite), ', fin_validite = ', quote_literal(src_fin_validite), ', updated_on = LOCALTIMESTAMP(0), deleted_on = null WHERE code = ', quote_literal(dest_code), ' ;');
            SQL = concat(SQL, ' UPDATE import_reg SET executed_on = LOCALTIMESTAMP(0) WHERE import_hash = ''', hash, ''' ;');
            INSERT INTO import_reg(operation, TABLE_NAME, source_code, field_name, to_value, from_value, SQL, created_on, import_hash) VALUES ('undelete', 'ztemptable', src_code, NULL, NULL, NULL, SQL, LOCALTIMESTAMP(0), hash);
        END IF;
     
        -- l'enregistrement existe dans la destination mais plus dans la source : il sera historisé
        IF (src_code IS NULL AND dest_code IS NOT NULL AND dest_deleted_on IS NULL) THEN
            operation = 'delete';
            hash = concat(concat(dest_code, '-'), import_hash);
            SQL = concat('UPDATE ztemptable SET deleted_on = LOCALTIMESTAMP(0) WHERE code = ', quote_literal(dest_code), ' ;');
            SQL = concat(SQL, ' UPDATE import_reg SET executed_on = LOCALTIMESTAMP(0) WHERE import_hash = ''', hash, ''' ;');
            INSERT INTO import_reg(operation, TABLE_NAME, source_code, field_name, to_value, from_value, SQL, created_on, import_hash) VALUES ('delete', 'ztemptable', src_code, NULL, NULL, NULL, SQL, LOCALTIMESTAMP(0), hash);
        END IF;
     
        RETURN operation;
    END; $Q$
    LANGUAGE plpgsql;

    NB: Pour un enregistrement existant à la fois dans la source et la destination (opération "update"), la détection de différence se fait colonne par colonne.

    Table IMPORT_REG, registre d'import

    Une table de travail import_reg est utilisée par le module pour y inscrire les opérations nécessaires à chaque synchronisation.

    Ce registre comporte les colonnes suivantes :

    • le type d'opération nécessaire : insert, update, delete, undelete
    • le "source code" de l'enregistrement concerné
    • le nom de la table concernée
    • le nom de la colonne concernée (si c'est pertinent pour l'opération)
    • la valeur de la colonne avant synchronisation (si c'est pertinent pour l'opération)
    • la valeur de la colonne après synchronisation (si c'est pertinent pour l'opération)
    • l'instruction SQL réalisant l'opération de mise à jour de la table destination
    • la date de création de la ligne de registre
    • la date d'exécution éventuelle de la ligne de registre (i.e. de l'instruction SQL)
    • hash permettant d'identifier cette ligne de registre pour mettre à jour sa date d'exécution

    Exemple de contenu de la table import_reg :

    operation source_code table_name field_name from_value to_value sql created_on executed_on import_hash
    insert 013CC021 progfin NULL NULL NULL INSERT INTO progfin(CODE, LIBELLE, FLECHE, DEBUT_VALIDITE, FIN_VALIDITE) VALUES (013CC021, Caract. verres trait, , 2017-05-01, 2018-12-31) ; UPDATE import_reg SET executed_on = now() WHERE import_hash = eb1ab85c-916a-11e7-aba7-0242f725575b ; 2017-09-04 14:17:41.501375 2017-09-04 14:17:41.549583 eb1ab85c-916a-11e7-aba7-0242f725575b
    insert 920CA050 progfin NULL NULL NULL INSERT INTO progfin(CODE, LIBELLE, FLECHE, DEBUT_VALIDITE, FIN_VALIDITE) VALUES (920CA050, Tthèse CIFRE2015/119, , 2016-01-01, 2018-12-31) ; UPDATE import_reg SET executed_on = now() WHERE import_hash = eb1ab85c-916a-11e7-aba7-0242f725575b ; 2017-09-04 14:17:41.501375 2017-09-04 14:17:41.549583 eb1ab85c-916a-11e7-aba7-0242f725575b
    insert 909CC189 progfin NULL NULL NULL INSERT INTO progfin(CODE, LIBELLE, FLECHE, DEBUT_VALIDITE, FIN_VALIDITE) VALUES (909CC189, THESE CIFRE, , 2016-02-01, 2019-12-31) ; UPDATE import_reg SET executed_on = now() WHERE import_hash = eb1ab85c-916a-11e7-aba7-0242f725575b ; 2017-09-04 14:17:41.501375 2017-09-04 14:17:41.549583 eb1ab85c-916a-11e7-aba7-0242f725575b
    insert 909EA002 progfin NULL NULL NULL INSERT INTO progfin(CODE, LIBELLE, FLECHE, DEBUT_VALIDITE, FIN_VALIDITE) VALUES (909EA002, ED SIMEM FONCTIONNEM, , 2015-01-01, 2047-12-31) ; UPDATE import_reg SET executed_on = now() WHERE import_hash = eb1ab85c-916a-11e7-aba7-0242f725575b ; 2017-09-04 14:17:41.501375 2017-09-04 14:17:41.549583 eb1ab85c-916a-11e7-aba7-0242f725575b
    insert 913CA052 progfin NULL NULL NULL INSERT INTO progfin(CODE, LIBELLE, FLECHE, DEBUT_VALIDITE, FIN_VALIDITE) VALUES (913CA052, ECO CORAIL, , 2014-09-26, 2017-12-31) ; UPDATE import_reg SET executed_on = now() WHERE import_hash = eb1ab85c-916a-11e7-aba7-0242f725575b ; 2017-09-04 14:17:41.501375 2017-09-04 14:17:41.549583 eb1ab85c-916a-11e7-aba7-0242f725575b
    insert 917CA955 progfin NULL NULL NULL INSERT INTO progfin(CODE, LIBELLE, FLECHE, DEBUT_VALIDITE, FIN_VALIDITE) VALUES (917CA955, CAPACITE, , 2013-11-01, 2047-12-31) ; UPDATE import_reg SET executed_on = now() WHERE import_hash = eb1ab85c-916a-11e7-aba7-0242f725575b ; 2017-09-04 14:17:41.501375 2017-09-04 14:17:41.549583 eb1ab85c-916a-11e7-aba7-0242f725575b
    insert F971UP03 progfin NULL NULL NULL INSERT INTO progfin(CODE, LIBELLE, FLECHE, DEBUT_VALIDITE, FIN_VALIDITE) VALUES (F971UP03, DELAWARE, , 2015-01-01, 2047-12-31) ; UPDATE import_reg SET executed_on = now() WHERE import_hash = eb1ab85c-916a-11e7-aba7-0242f725575b ; 2017-09-04 14:17:41.501375 2017-09-04 14:17:41.549583 eb1ab85c-916a-11e7-aba7-0242f725575b
    insert 925CD271 progfin NULL NULL NULL INSERT INTO progfin(CODE, LIBELLE, FLECHE, DEBUT_VALIDITE, FIN_VALIDITE) VALUES (925CD271, CONV.2015PCM51, , 2016-01-01, 2017-12-31) ; UPDATE import_reg SET executed_on = now() WHERE import_hash = eb1ab85c-916a-11e7-aba7-0242f725575b ; 2017-09-04 14:17:41.501375 2017-09-04 14:17:41.549583 eb1ab85c-916a-11e7-aba7-0242f725575b
    insert 925CD276 progfin NULL NULL NULL INSERT INTO progfin(CODE, LIBELLE, FLECHE, DEBUT_VALIDITE, FIN_VALIDITE) VALUES (925CD276, CONV.1060284, , 2015-12-10, 2019-12-31) ; UPDATE import_reg SET executed_on = now() WHERE import_hash = eb1ab85c-916a-11e7-aba7-0242f725575b ; 2017-09-04 14:17:41.501375 2017-09-04 14:17:41.549583 eb1ab85c-916a-11e7-aba7-0242f725575b
    insert 920CA047 progfin NULL NULL NULL INSERT INTO progfin(CODE, LIBELLE, FLECHE, DEBUT_VALIDITE, FIN_VALIDITE) VALUES (920CA047, Projet THYMOTHE, , 2015-12-21, 2018-12-31) ; UPDATE import_reg SET executed_on = now() WHERE import_hash = eb1ab85c-916a-11e7-aba7-0242f725575b ; 2017-09-04 14:17:41.501375 2017-09-04 14:17:41.549583 eb1ab85c-916a-11e7-aba7-0242f725575b
    undelete 013GR014 progfin NULL NULL NULL UPDATE progfin SET LIBELLE = SUBVENTION COTENTIN, FLECHE = , DEBUT_VALIDITE = 2017-01-01, FIN_VALIDITE = 2047-12-31, updated_on = now(), deleted_on = null WHERE CODE = 013GR014 ; UPDATE import_reg SET executed_on = now() WHERE import_hash = eb1ab85c-916a-11e7-aba7-0242f725575b ; 2017-09-04 14:17:41.501375 2017-09-04 14:17:41.549583 eb1ab85c-916a-11e7-aba7-0242f725575b
    undelete 012CB018 progfin NULL NULL NULL UPDATE progfin SET LIBELLE = Bilan sur les nouv., FLECHE = , DEBUT_VALIDITE = 2016-12-15, FIN_VALIDITE = 2018-12-31, updated_on = now(), deleted_on = null WHERE CODE = 012CB018 ; UPDATE import_reg SET executed_on = now() WHERE import_hash = eb1ab85c-916a-11e7-aba7-0242f725575b ; 2017-09-04 14:17:41.501375 2017-09-04 14:17:41.549583 eb1ab85c-916a-11e7-aba7-0242f725575b
    undelete 012RF001 progfin NULL NULL NULL UPDATE progfin SET LIBELLE = COLLOQUE CONSTITUTIO, FLECHE = , DEBUT_VALIDITE = 2017-01-01, FIN_VALIDITE = 2017-12-31, updated_on = now(), deleted_on = null WHERE CODE = 012RF001 ; UPDATE import_reg SET executed_on = now() WHERE import_hash = eb1ab85c-916a-11e7-aba7-0242f725575b ; 2017-09-04 14:17:41.501375 2017-09-04 14:17:41.549583 eb1ab85c-916a-11e7-aba7-0242f725575b
    update 971UP002 progfin LIBELLE Etablissement api Etablissement API UPDATE progfin SET LIBELLE = Etablissement API, updated_on = now() WHERE CODE = 971UP002 ; UPDATE import_reg SET executed_on = now() WHERE import_hash = eb1ab85c-916a-11e7-aba7-0242f725575b ; 2017-09-04 14:17:41.501375 2017-09-04 14:17:41.549583 eb1ab85c-916a-11e7-aba7-0242f725575b
    update 971UP004 progfin LIBELLE ETAB GIORGIA north ETAB GIORGIA NORTH UPDATE progfin SET LIBELLE = ETAB GIORGIA NORTH, updated_on = now() WHERE CODE = 971UP004 ; UPDATE import_reg SET executed_on = now() WHERE import_hash = eb1ab85c-916a-11e7-aba7-0242f725575b ; 2017-09-04 14:17:41.501375 2017-09-04 14:17:41.549583 eb1ab85c-916a-11e7-aba7-0242f725575b
    update 950ME003 progfin LIBELLE REGIE DE REC REGIE DE RECETTES-EN UPDATE progfin SET LIBELLE = REGIE DE RECETTES-EN, updated_on = now() WHERE CODE = 950ME003 ; UPDATE import_reg SET executed_on = now() WHERE import_hash = eb1ab85c-916a-11e7-aba7-0242f725575b ; 2017-09-04 14:17:41.501375 2017-09-04 14:17:41.549583 eb1ab85c-916a-11e7-aba7-0242f725575b
    ```

    Remarques :

    • Un "hash" (eb1ab85c-916a-11e7-aba7-0242f725575b par exemple) identifie un ensemble d'instructions SQL de mise à jour (cf. colonne import_hash) qui seront exécutées au sein d'une même transaction dans la base de données destination.
    • Chaque instruction SQL de mise à jour de la table destination (INSERT INTO progfin(... par exemple) est suivie d'une autre instruction SQL permettant d'inscrire dans le registre d'import la date d'exécution de la fameuse instruction de mise à jour de la table destination.*