, , Trigger sur insert

Vous voulez créer un trigger qui vous indique la création d’un enregistrement dans un fichier par exemple pour superviser, dans notre exemple on enverra un email , il est conseillé de faire un fichier de paramétrage

En CLLE soit le programme Alerte_msg


             PGM        PARM(&BUFFER &BUFLEN)             
/* Paramètres */                                          
             DCL        VAR(&BUFFER) TYPE(*CHAR) LEN(200) 
             DCL        VAR(&BUFLEN) TYPE(*CHAR) LEN(4)   
/* Variables de travail */                                
             DCL        VAR(&USR) TYPE(*CHAR) LEN(10)     
             DCL        VAR(&JOB) TYPE(*CHAR) LEN(10)     
             DCL        VAR(&NBR) TYPE(*CHAR) LEN(06)
             DCL        VAR(&EMAIL) TYPE(*CHAR) LEN(50)
             DCL        VAR(&SUJET) TYPE(*CHAR) LEN(100)
             DCL        VAR(&NOTES) TYPE(*CHAR) LEN(200)
             RTVJOBA    JOB(&JOB) USER(&USR) NBR(&NBR)    
             CHGVAR     VAR(&EMAIL) VALUE('votre@mail.fr')
             CHGVAR     VAR(&SUJET) VALUE('Enregistrement crée')
             CHGVAR     VAR(&NOTES) VALUE('Job :' +
                          *BCAT &NBR *TCAT '/' *TCAT &USR *TCAT '/' +
                          *TCAT &USR)                                    
 SNDSMTPEMM RCP((&EMAIL)) SUBJECT(&SUJET) NOTE(&NOTES) +
             CONTENT(*HTML)            
  MONMSG     MSGID(CPF0000) EXEC(GOTO CMDLBL(ERREUR))
goto fin                                                           
/* Gestion des erreurs  */                                         
erreur:                                                            
             SNDUSRMSG  MSG('Envoi email impossible pour msgint ' +
                          *bcat &job *bcat &usr *bcat &nbr ) +     
                          MSGTYPE(*INFO)                           
fin:                                                               
ENDPGM 

Pour attacher votre programme et enregistrer votre trigger

 ADDPFTRG   FILE(&LIB/REP_VALID) TRGTIME(*AFTER)     
              TRGEVENT(*INSERT) PGM(&LIB/ALERT_MSG) 
              TRGLIB(&LIB) 

&lib sera le nom de votre bibliothèque

En SQL ca créera un programme CEE et ca l’associera au trigger

CREATE OR REPLACE TRIGGER ALERTE_MSG                                   
 AFTER  INSERT ON REP_VALID                                            
 REFERENCING NEW AS N                                                  
 FOR EACH ROW                                                          
 MODE DB2ROW                                                           
-- email destinataire                                                  
 BEGIN                                                                 
DECLARE W_EMAIL CHAR(50);                                              
DECLARE W_SUJET CHAR(100);                                             
DECLARE W_NOTES CHAR(200);                                             
DECLARE EXIT HANDLER FOR SQLSTATE '38501'                              
 RESIGNAL SQLSTATE '38501' SET MESSAGE_TEXT = 'ENVOI MAIL IMPOSSIBLE.';
SET W_NOTES = 'Job : ' concat trim(N.REPNBR)                  
concat '/' concat trim(N.REPUSER) concat '/' concat trim(N.REPJOB) ;
SET W_EMAIL = 'votre@email.fr' ;                 
SET W_SUJET = 'Enregistrement crée' ;   
CALL QCMDEXC('SNDSMTPEMM RCP((''' concat trim(w_email) concat           
''')) SUBJECT(''' concat trim(replace(w_sujet , '''', '"'))             
concat ''') NOTE('''                                                    
concat trim(replace(W_NOTES , '''' , '"')) concat''') CONTENT(*HTML)') ;
END;  

Remarques :

Dans les 2 cas si l’utilisateur n’est pas inscrit à la liste de distribution votre email ne sera pas envoyé
c’est plus simple de gérer l’erreur en CLP.
Si vous devez accéder aux données du buffer ca sera plus rapide et plus simple en SQL ici n.zone

C’est des triggers après , puisque l’information doit être écrite dans tous les cas .

, , Afficher plusieurs colonnes d’enregistrements dans un sous-fichier

Contexte

Un sous-fichier nous permet d’afficher un nombre de lignes qui est limité par la taille de l’écran.
Cette taille est définie dans le script source de l’écran par le paramètre SFLPAG.

On possède un fichier que l’on souhaite afficher et qui contient plus de 19 enregistrement. Il serait donc intéressant de l’afficher sur plusieurs colonnes.

Solution

Une petite modification du script source permet de créer un sous fichier qui contient plusieurs colonnes. Il faut donc indiquer le nombre total de données que l’on souhaite voir à l’écran dans SFLPAG ainsi que le nombre de caractère qui séparent deux colonnes

La maquette se présente ainsi, le paramètre de SFLLIN correspond à l’espace (en caractères) entre deux colonnes.

En exécutant le programme, on obtient :

, , Intégrer Windows dans vos applications 5250

Vous avez des possibilités en standard sur votre IBMi :
Pour générer du PDF
Pour générer du CSV
Pour Générer du TXT

Comment rendre efficace et intégrer ces fichiers sous WINDOWS ?

1 ) Le profil utilisateur propose en standard un répertoire par défaut, /home/votreuser.

Vous devez créer cette directory qui deviendra alors votre répertoire par défaut.
La prise en compte est à la prochaine connexion
Vous pouvez contrôler en faisant
==> WRKLNK

2 ) Sur l’ibmi vous devez monter un partage, s’il n’existe pas encore sur le répertoire /home

3 ) Sur votre PC il est conseillé de monter un partage windows sur /home/votreuser chez nous lettre P.

4 ) Sur votre PC vous devrez vérifier que vos associations d’extension de fichier sont bien rattachées au bon logiciel

5 ) Vous devez ensuite générer le fichier

Exemple :

Génération d’un csv par les commandes de l’IBMI

CPYTOIMPF FROMFILE(QGPL/QAUOOPT)
TOSTMF(Liste_Options_PDM.csv)
MBROPT(REPLACE) STMFCCSID(PCASCII)
RCDDLM(CRLF) STRDLM(NONE) FLDDLM(‘;’)
ADDCOLNAM(*SQL)

Le fichier produit s’appelle Liste_Options_PDM.csv

Si vous allez dans le partage windows vous le voyez

Si on voulait boucler la boucle il faudrait pouvoir le lancer directement à partir de l’interface 5250

Vous disposez de 2 commandes coté IBMi pour faire

La première (STRPCO) va démarrer l’intégrateur qui va vous permettre de passer des commmandes sur votre pc

La deuxième (STRPCCMD) va exécuter la commande windows demandée

Ci joint un petit scripte pour enchainer tout ca ici c’est un programme indépendant, mais vous pouvez facilement le glisser dans un pgm de services

Je vous ai joint une commande pour habiller, si vous voulez tester

Le programme

**free
 // Paramètres
DCL-PI *N;
  P_Commande         CHAR(1023);  // IN / COMMANDE À EXECUTER
  P_Status           CHAR(02);    // OUT/ STATUS KO ET OK
END-PI;
if %parms() < 2 ;
dsply 'Ce programme nécessite 2 paramétres' ;
  *inlr = *on ;
  return ;
endif ;
  // démarrage de PCO
  exec sql call qsys2.qcmdexc('STRPCO') ;
  // démarrage de explorer
  exec sql
  call qsys2.qcmdexc('STRPCCMD PCCMD(''' concat :P_Commande  concat ''') PAUSE(*NO)') ;
  if sqlcode = 0 ;
    P_Status = 'OK'  ;
  else ;
    P_Status = 'KO'  ;
  endif ;
// Fin de programme
*inlr = *on ;

La commande

             CMD        PROMPT('Exécution commande windows')
             PARM       KWD(COMMANDE) TYPE(*CHAR) LEN(1023) +
                          DFT(EXPLORER) PROMPT('Commande windows')
             PARM       KWD(STATUS) TYPE(*CHAR) LEN(2) +
                          PMTCTL(*PMTRQS) PROMPT('Status sur +
                          l''exécution')

Exemple :

On demande l’ouverture du fichier généré

==>EXECWIND COMMANDE(‘P:LISTE_DES_OPTIONS_DE_PDM.CSV’)
P: lettre de partage
.CSV associé à excel

Conclusion :

Simple mais efficace
Ne pas oublier de faire du ménage dans les répertoires utilisateurs

, Nouvelle vue SYSMEMBERSTAT

Avec TR4 de la version V7R5 est arrivé la vue SYSMEMBERSTAT qui permet d’avoir des statistiques sur les membres, des informations supplémentaires à celles existantes à ce jour.

Voici un exemple :

SELECT TABLE_SCHEMA,
       TABLE_NAME,
       SYSTEM_TABLE_MEMBER,
       SOURCE_TYPE,
       LAST_CHANGE_TIMESTAMP,
       LAST_SAVE_TIMESTAMP,
       NUMBER_ROWS,
       OPEN_OPERATIONS,
       CLOSE_OPERATIONS,
       INSERT_OPERATIONS
    FROM QSYS2.SYSMEMBERSTAT
    WHERE table_schema = 'GMODERN'
          AND last_change_timestamp > CURRENT DATE - 1 MONTH
          AND SOURCE_TYPE IS NOT null
    ORDER BY last_change_timestamp DESC

Résultat

Rappel :

Il existe déjà une vue SYSPARTITIONSTAT qui donne sensiblement les mêmes informations

et ne rêver pas vous n’avez toujours pas le dernier utilisateur qui a modifié

, , Gérer les magasins de datas

Une des nouveautés de la dernière TR est de pouvoir analyser et exporter les analyses de journaux d’audit à partir d’un interface graphique dans navigator for i.

le menu

Vous avez la nouvelle option, gérer les magasins d’audit

vous devez commencer par en créer un

Attention la bibliothèque doit existée

Une fois les données agrégées vous avez une liste de vos magasins

vous avez un menu qui vous permet de voir le détail du contenu du magasin

Vous avez le détail de votre magasin que vous pouvez consulter ou exporter par exemple

le menu action

Conclusion

Ca facilite grandement l’utilisation et la restitution des informations d’audit

, , Gestion du suivi des tâches soumises

Une petite amélioration sur le clear avec la TR4

Je vous rappelle le principe de cette fonction qui existe depuis la TR1

C’est un job à démarrage automatique qui tourne dans QUSRWRK qui va superviser

QSYSWRK QSYS SBS 0,0 DEQW
QMRDBJNFY QSECOFR ASJ 0,0 PGM-QMRDBJNFY DEQW

Pour le lancer

QSYS/CALL QSYS/QMRDBJNFY PARM(0)

Ajouter une jobq à surveiller

CALL QSYS2.ADD_TRACKED_JOB_QUEUE(IASP_NAME => ‘*SYSBAS’,
JOB_QUEUE_LIBRARY => ‘majobq’,
JOB_QUEUE => ‘malib’);

Supprimer une jobq à surveiller

CALL QSYS2.REMOVE_TRAcked_job_QUEUE(IASP_NAME => ‘*SYSBAS’,
JOB_QUEUE_LIBRARY => ‘majobq’,
JOB_QUEUE => ‘malib’)


pour voir les jobq contrôlées

SELECT * FROM QSYS2.TRACKED_JOB_QUEUES

c’est le fichier QSYS/QAMRDJQL il peut être copié d’une machine à une autre !

Soumission d’un travail dans la file

SBMJOB CMD(DSPLIB LIB(QGPL)) JOB(DSPQGPL) JOBQ(GG/GGTEST)

voir le résultat

SELECT * FROM TABLE(QSYS2.TRACKED_JOB_INFO(JOB_QUEUE_LIBRARY_FILTER => ‘GG’,
JOB_QUEUE_FILTER => ‘GGTEST’))
ORDER BY INTERNAL_JOB_IDENTIFIER, ROUTING_STEP;

sortie

NODE_NAME QUALI00001 JOB_NAME JOB_USER JOB_NUMBER COMMA00001
NEPTUNE 740023/PLB/DSPQGPL DSPQGPL PLB 740023 DSPLIB LIB(QGPL)

pour clearer les logs
CALL QSYS2.CLEAR_TRACKED_JOB_INFO(IASP_NAME => ‘*SYSBAS’);

Vous pouvez avoir plus d’informations dans le fichier

Avec la TR4 vous avez des filtres supplémentaires par exemple jobq et bibliothèque

Pour clearer une jobq spécifique !

CALL QSYS2.CLEAR_TRACKED_JOB_INFO(IASP_NAME => ‘*SYSBAS’,
JOB_QUEUE_LIBRARY_FILTER => ‘GG’,
JOB_QUEUE_FILTER => ‘GGTEST’);

des liens à connaitre

https://www.ibm.com/docs/en/i/7.4?topic=environment-managing-submitted-job-tracker

https://www.ibm.com/support/pages/submitted-job-tracker-job-qmrdbjnfy

Remarque :

il definit des programmes d’exit que vous pouvez utiliser
Change Job (QIBM_QWT_CHGJOB)
Job Notification (QIBM_QWT_JOBNOTIFY)
Submit Job (QIBM_QWT_SBMJOB)

Rien dans Navigator for i Mais gros liens avec DB2MIRROR et son interface !

, , Manager les informations d’audit avec la TR4

Vous pouvez demander un tas d’informations d’audit pour tracer les violations de sécurité par exemple

Vous devez avoir le droit *AUDIT sur votre profil pour la mettre en place, c’est valeurs système qui permettent de le paramétrer les informations souhaitées

QAUDCTL, QAUDLVL, QAUDLVL2

Pour chaque information demandée, vous allez avoir un poste qui est créé dans le journal d’audit QAUDJRN.

Pour consolider ces informations, vous devez les copiez dans des fichiers il existait jusqu’à présent 2 méthodes

1) Les fichiers modèles utilisables dans des commandes IBMi

ils sont dans QSYS et commencent par QASY

DSPJRN QAUDJRN OUTPUT(*OUTFILE)
OUTFILE(MABIB/QASYPWJ5)
JRNCODE(‘T’) ENTTYP(‘PW’)

exemple pour les types PW, c’est le fichier QASYPWJ5 que vous devez dupliquer dans votre bibliothèque
DSPJRN JRN(QAUDJRN) ENTTYP(‘PW’)
OUTPUT(OUTFILE) OUTFILFMT(TYPE5) OUTFILE(MABIB/WASYPWJ5)

La stratégie est la suivante , la première fois vous demandez la création du fichier de sortie par un crtdupobj
et après vous ajoutez à ce fichier tous les soirs à 23 h 59 les postes PW

2) Les Technology Refresh nous apportent régulièrement des services

Ces services permettent de lire directement ces informations par SQL.
c’est les fonctions tables qui se trouve dans SYSTOOLS et qui s’écrivent SYSTOOLS.AUDIT_JOURNAL_XX()
XX étant le type
exemple pour les types PW
SYSTOOLS.AUDIT_JOURNAL_PW()

voici un exemple

select A.VIOLATION_TYPE_DETAIL, A.AUDIT_USER_NAME , A.REMOTE_ADDRESS, A.ENTRY_TIMESTAMP
from table (SYSTOOLS.AUDIT_JOURNAL_PW(STARTING_TIMESTAMP => current timestamp – 1 days)) A
order by A.ENTRY_TIMESTAMP desc

La stratégie est la suivante , la première fois vous demandez la création du fichier de sortie par un create table as()
et après vous ajoutez à ce fichier tous les soirs à 23 h 59 les postes PW par un insert

3) Avec la TR (4 pour V7R5 et 10 pour V7R4)

elle vous apporte une solution intégrée pour créer votre DATAMART
https://www.ibm.com/support/pages/ibm-i-75-tr4-enhancements
Vous avez 2 services

QSYS2.MANAGE_AUDIT_JOURNAL_DATA_MART procedure
QSYS2.AUDIT_JOURNAL_DATA_MART_INFO view

https://www.ibm.com/support/pages/node/7148888

ces 2 services vont faire ce que vous faisiez par SQL ou commande ibmi 2

Création du datamart le fichier résultat s’appelera AUDIT_JOURNAL_XX dans la bibliothèque que vous aurez choisi

Création de la table

CALL QSYS2.MANAGE_AUDIT_JOURNAL_DATA_MART(JOURNAL_ENTRY_TYPE => ‘PW’,
DATA_MART_LIBRARY => ‘MALIB’,
STARTING_TIMESTAMP => CURRENT DATE – 1 MONTH,
ENDING_TIMESTAMP => CURRENT TIMESTAMP);

Alimentation quotidienne de la table

CALL QSYS2.MANAGE_AUDIT_JOURNAL_DATA_MART(JOURNAL_ENTRY_TYPE => ‘PW’,
DATA_MART_LIBRARY => ‘MALIB’,
STARTING_TIMESTAMP => ‘*CONTINUE’,
ENDING_TIMESTAMP => CURRENT TIMESTAMP,
DATA_MART_ACTION => ‘ADD’
);

Vous avez une vue qui vous permet de suivre vos mises à jour de datamart

SELECT DATA_MART_LIBRARY, DATA_MART_TABLE, JOURNAL_ENTRY_TYPE, BUILD_END, FAILURE_DETAIL
FROM QSYS2.AUDIT_JOURNAL_DATA_MART_INFO
WHERE JOURNAL_ENTRY_TYPE = ‘PW’ AND DATA_MART_LIBRARY = ‘MALIB’ ;

Vous pourrez également manager par Navigator for i

Remarque :

Bien sur vous avez toujours aussi un onglet dans Navigator for i qui vous permet de voir sous forme de graphique les résultats , mais attention uniquement sur les postes en cours .
Si vous avez déjà une solution, c’est compliqué de revenir dessus, mais si vous n’avez rien ca vous simplifier considérablement les taches.
Attention dans tous les cas c’est à vous d’épurer les récepteurs quand vous les avez traité

, Rechercher dans l’IFS avec OMNIFIND

Vous trouvez que la recherche de fichier dans l’IFS par les services SQL n’est pas assez performante ;
Par exemple vous gérez des archives PDF sur l’IFS (toujours penser à épurer régulièrement).

Vous avez un logiciel gratuit qui s’appelle Omnifind, celui-ci permet de gérer des index textes sur l’IFS
mais aussi sur les spools si vous le désirez.

Notre besoin était sur l’IFS nous allons donc étudier ce cas au cours de cet article.

Nous avons déjà posté un autre article sur la mise en œuvre de Omnifind

Vous pouvez en savoir plus ici

https://www.ibm.com/support/pages/omnifind-ibm-i

Une fois que vous avez mis en place Omnifind et créé l’index qui va bien.

Vous retrouvez des procédures dans la collection que vous avez créée, chez nous elle s’appelle OMNIFIND.

Dans ces procédures vous en avez une qui s’appelle SEARCH qui va scanner dans vos fichiers
ceux qui contiennent une chaine de caractères.

Cette procédure renvoie un resultset ; Ce qui explique qu’en retour dans une interface ACS vous avez une liste et dans
un STRSQL juste un message de fin.

La problématique est donc de savoir comment traiter un resultset dans un programme avec, en plus, un résultat renvoyé dans un CLOB au format XML

Nous avons choisi de faire cet exercice en RPGLE.

Je vous joins un exemple ici, vous pouvez l’améliorer par exemple pour le parsage XML, ou la gestion des variables :

**free
//
// Options de compile
//
ctl-opt
   DFTACTGRP(*NO) ;
//
// Paramètre chaine à scanner
//
Dcl-Pi *N;
  p_texte char(30) ;
End-Pi;
//
// Initialisation des options de compile SQL
//
EXEC SQL
        Set Option
          Naming    = *SQL,
          Commit    = *None,
          UsrPrf    = *User,
          DynUsrPrf = *User,
          Datfmt    = *iso,
          CloSqlCsr = *EndMod;
// Zone du result set  pour la procedure SEARCH
dcl-s objtype char(10) ;
dcl-s objattr char(10) ;
dcl-s objlib  char(10) ;  // peut être nul int1
dcl-s objname char(10) ;  // peut être nul int2
dcl-s objinfo SQLTYPE(CLOB:65531)  ;
dcl-s objmod  TIMESTAMP  ;
dcl-s objtime int(5)     ;
// Déclaration de  l'adresse du result set
dcl-s RS1 SQLTYPE(RESULT_SET_LOCATOR) ;
// pour les variables à valeurs nulles
dcl-s Ind1 int(5)     ;
dcl-s Ind2 int(5)     ;
// Variables de travail
Dcl-S w_objinfo    Varchar(65531);
//
// Contrôle des paramètres
//
if %parms  = 0 ;
  dsply ('Vous devez saisir une recherche')  ;
  return ;
  *inlr = *on ;
endif;
// Appel de la procédure ici de la collection OMNIFIND
// la procédure est dans la collection qui contient l'index TXT
exec sql
   Call OMNIFIND.SEARCH(:p_texte);
// Si result set créé
if SQLCODE = +466;

// Vous associez le resultset de la procédure à votre curseur 
  exec sql ASSOCIATE LOCATORS (:RS1) WITH PROCEDURE OMNIFIND.SEARCH ;
  exec sql ALLOCATE C1 CURSOR FOR RESULT SET :RS1;
  // boucle de traitement du curseur associé
  exec sql fetch c1 into :objtype   ,
                         :objattr   ,
                         :objlib:ind1 ,
                         :objname:ind2 ,
                         :objinfo   ,
                         :objmod    ,
                         :objtime   ;
  //  ici traitement du fichier
  //     récupération des données du CLOB
  Traitement() ;
  dow sqlcode = 0;
    // traitement des variables lues...
    exec sql fetch c1 into :objtype   ,
                           :objattr   ,
                           :objlib:ind1,
                           :objname:ind2,
                           :objinfo   ,
                           :objmod    ,
                           :objtime   ;
    //     récupération des données du CLOB
    Traitement() ;
  //  ici traitement du fichier
  //
  ENDDO;
  exec sql close C1;
ENDIF;
*inlr = *on;
//
//  Procédure de traitement
//
dcl-proc Traitement ;
  dcl-s w_file    char(256);
  dcl-s pos1      int(5) ;
  dcl-s pos2      int(5) ;
  // dcl-s w_text    char(50);
  w_objinfo = %subst(objinfo_data : 1 : objinfo_len) ;
  //  traiter XML ca serait mieux
  // recherche <file_path>
  pos1 = %scan('<file_path>' : w_objinfo ) ;
  // recherche </file_path>
  pos2 = %scan('</file_path>' : w_objinfo ) ;
  w_file = %subst(w_objinfo : (pos1 + 11) : (pos2 - (pos1 + 11))) ;
//
// Traitement du fichier  w_file
//
//w_text = %subst(w_file   : 1 : 50) ;
//dsply w_text ;
end-proc ; 


Les points importants à comprendre :

C’est la déclaration et l’utilisation d’un curseur basé sur un resultset – j’ai mis un commentaire dans le source.

Et la manipulation d’une variable CLOB.

Voila les points principaux à retenir :

Quand vous déclarez une dans votre programme RPGLE votre variable CLOB,

DCL-S XXX SQLTYPE(CLOB:nnnn);

En réalité, après traduction c’est déclaré comme ça :

DCL-DS XXX;
XXX_LEN UNS(10);
XXX_DATA CHAR(nnnn) CCSID(*JOBRUNMIX);
END-DS XXX;

nnnn étant la longueur de la zone
XXX_LEN contient la longueur du contenu de la zone
XXX_DATA contient la donnée

Dans la partie traitement vous pouvez facilement faire un QCMDEXC en SQL, par exemple pour faire une copie, un envoi ou une suppression

Voila un exemple concret que vous pouvez adapter, n’hésitez pas à tester Omnifind car il est gratuit et comme on met de plus en plus de chose dans l’IFS, il est de plus en plus utile !

, , , Convertir un SAVF en PF

il faut savoir qu’un fichier SAVF est un fichier PF de 528 de long.

Vous pouvez avoir besoin pour différentes raisons de convertir SAVF en PF pour manipuler plus simplement par SQL, pour contourner des restrictions d’utilisations etc ..

Vous trouverez ici 2 commandes qui vont vous permettent de réaliser cette opération : https://github.com/Plberthoin/PLB/tree/master/CPYSAVF

Copier un SAVF dans un PF

Voir le PF

Copier un PF dans un SAVF

Voir le Résultat

Pour voir ce résultat taper la commande DSPSAVF , Voir WRKSAVF si vous avez installez notre outil

Remarque :

Outil simple mais efficace qui peut rendre des services à des administrateurs

Vous voulez mettre à jour des informations dans une table sans faire de boucle de lecture ? Cet article est pour vous.

Prenons un exemple. Vous consommez un service web qui vous transmet une liste d’article que vous devez stocker en base de données.

L’ordre DB2 MERGE vous permettra d’ajouter les articles qui n’existent pas et de mettre à jour ceux qui existent.

Création de table ARTICLE

CREATE TABLE ARTICLE ( 
	CDART CHAR(10) CCSID 1147 DEFAULT ' ' , 
	DESART CHAR(30) CCSID 1147 DEFAULT ' ' )   
	  
	RCDFMT ARTICLE    ; 
  
LABEL ON TABLE ARTICLE 
	IS 'Base article' ; 
  
LABEL ON COLUMN ARTICLE 
( CDART IS 'Code article         ' , 
	DESART IS 'Désignation article  ' ) ;

Code RPG avec ajout ou mise à jour d’un article.

ctl-opt alwnull(*usrctl) option(*nodebugio:*srcstmt);

//Variables
dcl-s code_article        char(10);
dcl-s designation_article char(30);

exec sql SET OPTION commit = *none;

code_article        = 'AB123456';
designation_article = 'Ma désignation';

Exec Sql
   MERGE INTO ARTICLE as ARTICLE
   USING(VALUES (:code_article, :designation_article))
   AS SOURCE(CDART,DESART)
   ON ARTICLE.CDART = SOURCE.CDART
   -- Mise à jour de la désignation si code article trouvé
   WHEN MATCHED THEN UPDATE
         SET DESART  = SOURCE.DESART
   -- Ajout de l'article
   WHEN NOT MATCHED THEN INSERT VALUES (SOURCE.CDART,
                                        SOURCE.DESART)
   NOT ATOMIC CONTINUE ON SQLEXCEPTION;


*inLR=*ON;

Vous pouvez également supprimer l’article s’il est trouvé.

WHEN MATCHED THEN DELETE

On peut rajouter des conditions en plus du MATCHED. Dans la ligne ci-dessous je ne veux mettre à jour que l’article ‘TEST’. Pas grand intérêt mais c’était pour montrer un exemple.

WHEN MATCHED AND CDART='TEST' THEN UPDATE SET DESART  = SOURCE.DESART

Vous pouvez aussi tester la valeur pour la contrôler avant de faire la mise à jour.

WHEN MATCHED AND SOURCE.DESART IS NOT NULL THEN UPDATE SET DESART  = SOURCE.DESART

Il est également possible de prendre le résultat d’un select au lieu des deux variables

MERGE INTO ARTICLE as ARTICLE
   USING(VALUES (SELECT CDART, DESART FROM ARTICLE2))
   AS SOURCE(CDART,DESART)
   ON ARTICLE.CDART = SOURCE.CDART
   -- Mise à jour de la désignation si code article trouvé
   WHEN MATCHED THEN UPDATE
         SET DESART  = SOURCE.DESART
   -- Ajout de l'article
   WHEN NOT MATCHED THEN INSERT VALUES (SOURCE.CDART,
                                        SOURCE.DESART)
   NOT ATOMIC CONTINUE ON SQLEXCEPTION;

Je ne l’ai pas dit au début de l’article mais ça fonctionne aussi sur les PF.