, Réorganiser un très gros fichier

Voici la problématique qui nous est arrivée :

Suite à un gros coup de ménage sur une petite machine saturée avec un énorme fichier de 12 millions d’enregistrement plus 30 millions d’enregistrements supprimés et 4 index dessus

Le réflexe naturel, a été le bon vieux RGZPFM qui devait nous réorganiser tout ça … eh bien non. On n’a jamais réussi à le lancer jusqu’au bout.

Voici comment nous avons fait pour nous en sortir. Nous avons utilisé une des possibilités du CPYF qui élimine les enregistrements supprimés.

En gros voici les opérations à réaliser


1) Copie vers un fichier de sauvegarde de votre fichier
2) Suppression des index pour éviter de saturer le système en reconstruisant tous les index en même temps
3) Mise à blanc du fichier initial
4) Recopie des données depuis votre sauvegarde
5) Remise des index initiaux

Vous pouvez retrouver un exemple de source qui fait ça ici https://github.com/Plberthoin/PLB/tree/master/GTOOLS

Remarque :


Dans cet outil l’astuce utilisée pour la suppression des index ne marche que sur les LF et pas sur les index (le plus simple dans ce cas la est d’extraire le source , droper l’index et rejouer votre script SQL)
On doit pouvoir également faire une copie par insert into … select
Il existe certainement d’autres alternatives …

, Utilisation QAQQINI

Le fichier QAQQINI sert à paramétrer les exécutions SQL pour un travail , et donc à donner des directives spécifiques sur les exécutions de requêtes, pour un travail donné.

On ne verra pas ici toutes les options disponibles à ce jour, mais on présentera le principe.

Celui qui est utilisé par défaut, c’est celui que QUSRSYS
Vous avez le message bien connu dans vos logs

Vous pouvez dupliquer ce fichier par ==>CRTDUPOBJ, puis le customiser par SQL,

Vous pouvez alors changer ce fichier pour votre travail en indiquant la bibliothèque qui contient le fichier QAQQINI souhaité

Par la commande CLP

==>CHGQRYA QRYOPTLIB(VOTREBIB)

En sql en utilisant la procédure OVERRIDE_QAQQINI

Création d'un fichier QAQQINI dans QTEMP
Call  override_qaqqini(‘1’ , ‘ ‘ , ‘ ‘)

Modification des valeurs le job ici pour utiliser les MQTs 
Call  override_qaqqini(‘2’ , ‘MATERIALIZED_QUERY_TABLE_REFRESH_AGE‘ , ‘*ANY‘)
Call  override_qaqqini(‘2’ , ‘MATERIALIZED_QUERY_ TABLE_USAGE‘ , ‘*ALL‘)

Suppression de qaqqini de QTEMP,  si nécessaire
Call  override_qaqqini(‘3’ , ‘ ‘ , ‘ ‘)       

Attention le profil qui exécute doit à voir *JOBCTL (gestion des travaux)

La table est livrée avec *DEFAULT dans tous les paramètres

Pour comprendre la valeur *DEFAULT ci joint une table qui contient les valeurs décryptées
Création de la table des valeurs par défauts

CREATE TABLE GAIA/QAQQINDFT (
QQPARM VARCHAR(256) ALLOCATE(10) CCSID 297 NOT NULL ,
QQVAL VARCHAR(256) ALLOCATE(10) CCSID 297 NOT NULL )
RCDFMT QAQQINDFT ;
LABEL ON COLUMN GAIA/QAQQINDFT
( QQPARM IS ‘Parameter’ ,
QQVAL IS ‘Parameter Value’ ) ;
LABEL ON COLUMN GAIA/QAQQINDFT
( QQPARM TEXT IS ‘Query option parameter’ ,
QQVAL TEXT IS ‘Query option parameter value’ ) ;

Insérer les valeurs correspondantes aux valeurs par défaut

insert into GAIA/QAQQINDFT VALUES(‘APPLY_REMOTE’, ‘YES’) ;

insert into GAIA/QAQQINDFT VALUES(‘PARALLEL_DEGREE’, ‘OPTIMIZE’) ;
insert into GAIA/QAQQINDFT VALUES(‘ASYNC_JOB_USAGE’, ‘LOCAL’) ;

insert into GAIA/QAQQINDFT VALUES(‘QUERY_TIME_LIMIT’, ‘NOMAX’) ;
insert into GAIA/QAQQINDFT VALUES(‘UDF_TIME_OUT’, ’30’) ;
insert into GAIA/QAQQINDFT VALUES(‘MESSAGES_DEBUG’, ‘NO’) ;

insert into GAIA/QAQQINDFT VALUES(‘PARAMETER_MARKER_CONVERSION’, ‘YES’) ;
insert into GAIA/QAQQINDFT VALUES(‘OPEN_CURSOR_THRESHOLD’, ‘0’) ;
insert into GAIA/QAQQINDFT VALUES(‘OPEN_CURSOR_CLOSE_COUNT’, ‘0’) ;
insert into GAIA/QAQQINDFT VALUES(‘OPTIMIZE_STATISTIC_LIMITATION’, ‘Calculez par l’optimiseur’) ;
insert into GAIA/QAQQINDFT VALUES(‘OPTIMIZATION_GOAL’, ‘Dans l’interface’) ;
insert into GAIA/QAQQINDFT VALUES(‘FORCE_JOIN_ORDER’, ‘NO’) ;

insert into GAIA/QAQQINDFT VALUES(‘COMMITMENT_CONTROL_LOCK_LIMIT’, ‘500000000’) ;

insert into GAIA/QAQQINDFT VALUES(‘REOPTIMIZE_ACCESS_PLAN’, ‘NO’) ;
insert into GAIA/QAQQINDFT VALUES(‘SQLSTANDARDS_MIXED_CONSTANT’, ‘YES’) ;

insert into GAIA/QAQQINDFT VALUES(‘SYSTEM_SQL_STATEMENT_CACHE’, ‘YES’) ;
insert into GAIA/QAQQINDFT VALUES(‘IGNORE_LIKE_REDUNDANT_SHIFTS’, ‘OPTIMIZE’) ;

insert into GAIA/QAQQINDFT VALUES(‘STAR_JOIN’, ‘NO’) ;
insert into GAIA/QAQQINDFT VALUES(‘SQL_SUPPRESS_WARNINGS’, ‘NO’) ;

insert into GAIA/QAQQINDFT VALUES(‘SQL_TRANSLATE_ASCII_TO_JOB’, ‘NO’) ;
insert into GAIA/QAQQINDFT VALUES(‘NORMALIZE_DATA’, ‘NO’) ;

insert into GAIA/QAQQINDFT VALUES(‘LOB_LOCATOR_THRESHOLD’, ‘0’) ;

insert into GAIA/QAQQINDFT VALUES(‘MATERIALIZED_QUERY_TABLE_USAGE’, ‘0’) ;

insert into GAIA/QAQQINDFT VALUES(‘MATERIALIZED_QUERY_TABLE_REFRESH_AGE’, ‘NONE’) ;
insert into GAIA/QAQQINDFT VALUES(‘ALLOW_TEMPORARY_INDEXES’, ‘YES’) ;

insert into GAIA/QAQQINDFT VALUES(‘VARIABLE_LENGTH_OPTIMIZATION’, ‘YES’) ;
insert into GAIA/QAQQINDFT VALUES(‘CACHE_RESULTS’, ‘SYSTEM’) ;

insert into GAIA/QAQQINDFT VALUES(‘LIMIT_PREDICATE_OPTIMIZATION’, ‘NO’) ;
insert into GAIA/QAQQINDFT VALUES(‘STORAGE_LIMIT’, ‘NOMAX’) ;

insert into GAIA/QAQQINDFT VALUES(‘SQL_DECFLOAT_WARNINGS’, ‘NO’) ;
insert into GAIA/QAQQINDFT VALUES(‘SQL_STMT_COMPRESS_MAX’, ‘2’) ;
insert into GAIA/QAQQINDFT VALUES(‘SQL_FAST_DELETE_ROW_COUNT’, ‘0’) ;
insert into GAIA/QAQQINDFT VALUES(‘SQL_STMT_REUSE’, ‘3’) ;
insert into GAIA/QAQQINDFT VALUES(‘SQL_CONCURRENT_ACCESS_RESOLUTION’, ‘WAIT’) ;

insert into GAIA/QAQQINDFT VALUES(‘SQL_XML_DATA_CCSID’, ‘1208’) ;

insert into GAIA/QAQQINDFT VALUES(‘FIELDPROC_ENCODED_COMPARISON’, ‘ALLOW_EQUAL’) ;
insert into GAIA/QAQQINDFT VALUES(‘SQL_MODIFIES_SQL_DATA’, ‘NO’) ;

insert into GAIA/QAQQINDFT VALUES(‘ALLOW_ARRAY_VALUE_CHANGES’, ‘NO’) ;
insert into GAIA/QAQQINDFT VALUES(‘ALLOW_ADAPTIVE_QUERY_PROCESSING’, ‘YES’) ;

insert into GAIA/QAQQINDFT VALUES(‘COLLATE_ERRORS’, ‘NO’) ;
insert into GAIA/QAQQINDFT VALUES(‘SQL_PSEUDO_CLOSE’, ‘QSQPSCLS1 DTAARA’) ;

insert into GAIA/QAQQINDFT VALUES(‘MEMORY_POOL_PREFERENCE’, ‘JOB’) ;
insert into GAIA/QAQQINDFT VALUES(‘TEXT_SEARCH_DEFAULT_TIMEZONE’, ‘UTC’) ;
insert into GAIA/QAQQINDFT VALUES(‘SQE_NATIVE_ACCESS_POSITION_BEHAVIOR’, ‘Normal positioning behavior is performed’) ;
insert into GAIA/QAQQINDFT VALUES(‘SQL_SUPPRESS_MASKED_DATA_DETECTION’, ‘NO’) ;

insert into GAIA/QAQQINDFT VALUES(‘SYSTIME_PERIOD_ADJ’, ‘ERROR’) ;
insert into GAIA/QAQQINDFT VALUES(‘CONCURRENT_ACCESS_BEHAVIOR’, ‘OPTIMIZE’) ;

insert into GAIA/QAQQINDFT VALUES(‘ALLOW_EVI_ONLY_ACCESS’, ‘YES’) ;
insert into GAIA/QAQQINDFT VALUES(‘PSEUDO_OPEN_CHECK_HOST_VARS’, ‘*NO’)

Quelques requêtes SQL pour faire votre analyse

Voir les valeurs par défaut décryptées

SELECT substr(QQPARM, 1, 40) as Parametre, substr(QQVAL, 1, 40) as
valeur FROM gdata/qaqqindft

Voir les valeurs modifiées sur le système dans qusrsys par exemple

SELECT substr(QQPARM, 1, 40) as Parametre, substr(QQVAL, 1, 40) as
valeur FROM qusrsys/qaqqini where qqval <> ‘*DEFAULT’

Voir les fichiers QAQQINI présents sur la machine

SELECT OBJNAME, objlib , X.LAST_USED_TIMESTAMP FROM TABLE (QSYS2.OBJECT_STATISTICS(‘*ALL’,’FILE’,’QAQQINI’)) X

pour voir toutes les valeurs actives dans qaqqini

SELECT ‘DFT’ as type , substr(A.QQPARM, 1, 40) as Parametre,
substr(b.QQVAL, 1, 40) as
valeur FROM gdata/qaqqini A join gdata/qaqqindft b on
A.QQPARM = b.QQPARM
where a.QQVAL = ‘DEFAULT’

union

SELECT ‘CST’ as type, substr(A.QQPARM, 1, 40) as Parametre, substr(A.QQVAL, 1, 40) as valeur

FROM gdata/qaqqini A where a.QQVAL <> ‘DEFAULT’
order by parametre

Référence pour les valeurs et leur signification ici

https://www.ibm.com/docs/en/i/7.4?topic=qaqqini-query-options

Attention :

Ces modifications peuvent avoir des effets désastreux sur les performances, essayer de faire des analyses précises par Visual explain ou l’analyse du plan cache.
Et le cas échéant prévoyez un rollback rapide

Exemple
update gdata.qaqqini set qqval = ‘*DEFAULT’ where qqparm = ‘MATERIALIZED_QUERY_TABLE_REFRESH_AGE’

, Utilisez une table MQT

Une table de requête matérialisée (MQT Materialized Query Table) est une table dont la définition est basée sur le résultat d’une requête. Les données contenues dans un MQT sont dérivées d’une ou plusieurs tables sur lesquelles la définition de la table de requête matérialisée est basée.

Cette solution est assez peu utilisée sur l’IBMi mais beaucoup plus sur DB2 Universal.

Pour créer une MQT create table grefer.lstsrc_mqt as ( votre requête )

DATA INITIALLY IMMEDIATE
REFRESH DEFERRED
ENABLE QUERY OPTIMIZATION
MAINTAINED BY USER;

Ces options peuvent changer, vérifiez dans les documentations IBM, par exemple ici https://www.ibm.com/docs/en/i/7.4?topic=database-overview

exemple ici :

En sélectionnant la bibliothèque, vous pouvez avoir plusieurs tables et utiliser des requêtes très compliquées.

create table grefer.lstsrc_mqt as
(select SRCLIB, SRCFIL, SRCMBR, SRCTYP, SRCSEQ, SRCDTA, SRCDAT from grefer.lstsrc
where srclib = ‘GSEND’)
DATA INITIALLY IMMEDIATE
REFRESH DEFERRED
ENABLE QUERY OPTIMIZATION
MAINTAINED BY USER;

Ça peut être une bonne solution pour les grosses extractions, c’est très intéressant pour de la BI par exemple.

Vous l’utilisez ensuite comme une table normale.

select * from grefer.lstsrc

Quand vous décidez de mettre à jour vos données vous utiliserez un refresh ce qui sera plus rapide qu’une régénération de table ou qu’une nouvelle exécution.

Exemple
refresh table grefer.lstsrc_mqt

plus d’informations ici https://developer.ibm.com/articles/dm-0509melnyk/

, Génération CSV à partir de SQL

Il existe plusieurs méthodes pour faire du CSV, la solution la plus connue est la commande CPYTOIMPF.

Les procédures SQL peuvent offrir une alternative intéressante dans certains cas, voici un exemple.

Cette exemple utilise « Dynamic Compound Statement » avec la procédure QSYS2.IFS_WRITE_UTF8

quauoopt est le fichier des options PDM

BEGIN
— Génération fichier + entête
CALL QSYS2.IFS_WRITE_UTF8(PATH_NAME =>’/tmp/qauoopt.csv’,
LINE => ‘Option;Commande’,
OVERWRITE => ‘REPLACE’,
END_OF_LINE => ‘NONE’);
— Boucle de traitement des lignes
FOR SELECT option concat ‘;’ concat command as TEXTE From QGPL.QUAUOOPT DO
CALL QSYS2.IFS_WRITE_UTF8(PATH_NAME => ‘/tmp/qauoopt.csv’,
LINE => TEXTE);
END FOR;
END

Remarque :

Cette solution ne peut pas s’appliquer partout , en effet un « ; » dans une zone pourrait poser un problème à la lecture.
A l’inverse vous pouvez ajouter des instructions comme dans la fichiers XLS par exemple.

Il y a plusieurs procédures qui vous permettent de gérer l’IFS, et ca nous simplifie la tache.

pour retrouver les informations sur SQL services, https://www.ibm.com/support/pages/ibm-i-services-sql

, Extraire simplement vos références Web Services <-> Programme (de service)

Nous développons de plus en plus de web services grâce à IWS (Integrated Web Services).

L’implémentation de ces services peut être un programme, un programme de service, ou SQL. Par ailleurs, les deux dernières solutions nous permettent d’avoir des services avec plusieurs opérations (vocabulaire SOAP) ou routes (vocabulaire REST). Quoiqu’il en soit, plusieurs actions possibles au travers d’un unique service.

La question se pose désormais à plus grande échelle des impacts sur la maintenance des programmes et programmes de service sous-jacents à des services web !

Concrètement : je modifie un programme (de service) : comment savoir s’il est exposé en tant que service web ?

Scripts fournis

IWS est fourni avec des scripts, dans le répertoire /QIBM/ProdData/OS/WebServices/bin

Ces scripts permettent d’automatiser toutes les actions possibles, autrement disponibles via l’interface d’administration :

Nous vous recommandons leur usage pour déployer vos services par exemple, de façon automatique avec vos outils.

Ces scripts sont également capables d’extraire des informations des serveurs et services !

Extraction

Pour le principe ici, un script shell qui utilise ces fonctionnalités pour lister tous les services et récupérer tous les programmes (de service) référencés ! Le résultat est stocké en BD. C’est perfectible, mais démontre le fonctionnement :

#
# Extraire les références croisées web service <-> programme/programme de service
#

# Paramètres : bibliothèque de création du fichier 

export PATH=$PATH:/QIBM/ProdData/OS/WebServices/bin

# créer le fichier BD pour stocker les liens
db2 "create or replace table $1.wsxref (server varchar(10), service varchar(128), program varchar(128)) on replace delete rows"

# Lister les serveurs sans tenir compte de l'état (started/stopped)
servers=$(listWebServicesServers.sh | sed 's/(.*//' )
# Pour chaque serveur listé
for server in $servers ; do
  # Retourver les services sans tenir compte de l'état
  services=$(listWebServices.sh -server $server | sed 's/(.*//')
  # Pour chaque service
  for service in $services ; do
#   # Retrouver les propriétés. Ne garder que l'implémentation
	program=$(getWebServiceProperties.sh -server $server -service $service | grep -i 'Program object path:' | sed 's/Program object path://')
	echo $server $service $program
    db2 "insert into $1.wsxref values('$server', '$service', trim('$program'))"
  done
done

Le code est disponible ici : https://github.com/FrenchIBMi/webservices/blob/master/wsxref.sh

Une fois le script lancé (prévoyez un café le temps de l’exécution) :

Et voilà !

Vous pouvez l’appeler dans QSH, le planifier (commande QSH …), le mettre dans un CL. Et surtout intégrer le résultat dans vos outils d’analyse !

, , Joblog pending

Sur votre système, il peut rester des logs à l’état pending, ce n’est pas normal, c’est des logs qui ne sont pas accessibles, mais qui sont là …

Vous pouvez facilement regarder cette information grâce à la vue QSYS2.SYSTEM_STATUS_INFO

Vous devez d’abord vérifier la valeur système qui indique la production de ces logs.
c’est QLOGOUTPUT qui doit être à *JOBLOGSVR et non à *PND qui indiquerait au système de les laisser dans cet état là.

Pour supprimer ces spools inutiles vous devrez utiliser l’API QWTRMVJL

Voici un exemple d’utilisation vous pouvez la passer comme ceci

CALL PGM(QWTRMVJL) PARM(X’0000002C000000005CC1D3D34040404040405CC1D3D34040
404040405CC1D3D340405CC1D3D3404040404040′ ‘RJLS0100′ X’0000000000000000’)

Si vous repassez la requête pour contrôler :

select JOBQ_JOB_TABLE_ENTRIES, OUTQ_JOB_TABLE_ENTRIES, JOBLOG_PENDING_JOB_TABLE_ENTRIES
from QSYS2.SYSTEM_STATUS_INFO

Vous avez alors 0 dans la colonne JOBLOG_PENDING_JOB_TABLE_ENTRIES

Remarque

Si ça revient souvent vous devrez comprendre pourquoi ce phénomène se produit …

, Sauvegarde des objets modifiés

Vous connaissez tous les commandes


SAVLIB LIB(&LIB), pour sauvegarder une bibliothèque et ses objets
SAVOBJ OBJ(*ALL) LIB(&LIB), pour sauvegarder tous les objets de votre bibliothèque

il existe une commande qui permet de ne sauvegarder que les objets modifiés, c’est la commande SAVCHGOBJ
SAVCHGOBJ OBJ(*ALL) LIB(&LIB) REFDATE(&DATREF) REFTIME(&HREREF)

Vous devrez indiquer une date/heure qui va servir de limite à partir de laquelle vous allez considérer que votre objet est modifié.

Par défaut il prendra la date de dernière sauvegarde de la bibliothèque
vous pouvez voir cette date dans un DSPOBJD *libl/votrebib *LIB, par exemple

Voici, un exemple d’utilisation, vous désirez avoir un environnement de test à j-1, bien que cela varie en fonction des applications et des clients, on estime qu’on ne modifie que 1/3 des fichiers chaque jour.

Vous mettez en place une sauvegarde/restauration des objets modifiés à partir de la date de la veille (vous devrez gérer cette date, dans une dtaara par exemple …)

La limite est que si vous avez des très grosses tables qui sont changées tous les jours, elles seront renvoyées systématiquement …

Remarque

La commande SAVRSTCHG existe dans OBJECT CONNECT, elle permet d’enchainer la sauvegarde et la restauration, elle fonctionne sous TCPIP à partir de la version V7R4.

Comme les objets risquent d’exister, pensez aux options … MBROPT(ALL) … ALWOBJDIF(ALL)

Il est difficile d’avoir une stratégie globale basée sur les savchg, ça complique considérablement les opérations de restaurations

Comment transférer un job dans qctl

Il peut être important de lancer un job qui tourne même quand le système est en mode restreint

Nous supposerons bien sûr que le sous-système de contrôle est QCTL.

1) Un job batch

il suffit d’indiquer la jobq QCTL dans le SBMJOB

exemple :

Vous avez une commande qui fait une SAV21

SBMJOB CMD(SAV21)
JOB(SAV21)
JOBQ(QCTL)

2) Pour un interactif , si le job est actif

TFRJOB JOBQ(QCTL)

3) Pour un interactif, si le job n’est pas actif

Vous devrez anticiper ce risque dés à présent en créant un unité qui a une entrée nominative dans QCTL.

pour créer l’unité

Récupérer le dernier contrôleur *VWS créer
WRKCTLD

CRTDEVDSP DEVD(votre unité ) DEVCLS(VRT) TYPE(3477) MODEL(FC) ONLINE(NO) CTL(votre controleur) KBDTYPE(FAB) CHRID(697 297)
ALWBLN(YES) PRTDEV(SYSVAL) OUTQ(DEV) PRTFILE(LIBL/QSYSPRT)
TEXT(‘Unité SECOURS’) DEPLOCNAME(*NONE)

créer l’entrée écran dans QCTL

ADDWSE SBSD(QCTL)
WRKSTN(votre écran)

pour contrôler

DSPSBSD
puis option 4

Vous devez retrouver une entrée avec votre nom d’unité

Maintenant vous devez indiquer votre nom d’unité dans votre session
exemple sur ACS

A la mire la mire vous devez avoir ceci

Attention

Ça peut vous sauver de petites blague, mais ça ne doit pas être un mode fonctionnement systématique.

, , , Préciser la bibliothèque dans un SELECT INTO

Vous avez dans un programme RPGLE un select into en SQL à faire, mais vous devez choisir la bibliothèque ou se trouve le fichier !

Voici 3 solutions pour éviter le hard codage

1ére solution

Cette solution est plus adaptée à un curseur.

Formater une variable exemple sqlstm

sqlstm = ‘select votrezone from ‘ + votre_lib + ‘/votre_table’ ;

exec SQL
declare curs01 Cursor
for sqlstm ;

exec SQL
prepare sqlstm
from :sqlstm ;

exec SQL
open curs01 ;

exec sql
fetch next
from curs01
into :votrezone ;

On est d’accord, c’est un peu lourd

Voici une alternative

Si vous êtes en convention de nommage *SYS, vous pouvez utiliser la liste de bibliothèques

exec sql
call qcmdexc( ‘addlible ‘ concat :votre_lib) ;
// select sans bibliothèque
exec sql
select mazone into : wmazone from matable ;

Si vous êtes en convention de nommage *SQL, vous devez préciser le schéma courant – par défaut c’est le profil utilisateur.
// select sans bibliothèque
exec sql
set schema :votre_lib ;

exec sql
select mazone into : wmazone from matable ;

A noter que dans la cas d’une convention de nommage *SQL, toutes vos tables doivent être dans la même bibliothèque, en cas de jointure par exemple

Il existe effectivement une autre alternative avec dynamic SQL,

Merci Birgitta pour cette précision .

Avec dynamic SQL et VALUES … INTO:
Exemple:
CmdSQL = ‘Values( Select Count(*) from ‘ + %Trim(YourSchema) + ‘.YourTable ‘ +
‘ ) into ?’;

Exec SQL Prepare DynSQL from :CmdSQL;
Exec SQL Execute DynSQL using :YourResult;

, , Zones numériques étendues

Voici une petite expérience à faire sur les zones étendues

Commençons par créer un fichier PF comme ceci

A R TESTAF
A NUMERO 5
A NOM 30
A PRENOM 30

Compilez-le

on lui met des données

par exemple par SQL

INSERT INTO GDATA/TESTA VALUES(‘AAAAA’, ‘Carlsen’, ‘Magnus’)
INSERT INTO GDATA/TESTA VALUES(‘BBBBB’, ‘Vachier-Lagrave’, ‘Maxime’)
INSERT INTO GDATA/TESTA VALUES(‘CCCCC’, ‘Firouzja’, ‘Alireza’)

Si on fait un DSPPFM, on voit ceci

On décide de changer NUMERO et de le passer en numérique

On a donc le fichier décrit comme suit

A R TESTNF
A NUMERO 5S 0
A NOM 30
A PRENOM 30

Vous le compilez

vous décidez de récupérer les données

par un CPYF

CPYF FROMFILE(TESTA)
TOFILE(TESTN)
MBROPT(*REPLACE) FMTOPT(*NOCHK)

vous faites DSPPFM du fichier TESTN, voici le résultat :

Vous faites un select * from TESTN voici le résultat :

C’est étonnant non ?

Si vous faites un contrôle de données vous n’avez pas d’erreur

SELECT * FROM TABLE(SYSTOOLS.VALIDATE_DATA_FILE(‘GDATA’, ‘TESTN’));

Vous allez me dire pourquoi ne pas faire le nouveau fichier comme une table, allons y ?

CREATE TABLE GDATA.TESTS (
NUMERO NUMERIC(5, 0) NOT NULL DEFAULT 0 ,
NOM CHAR(30) CCSID 1147 NOT NULL DEFAULT  » ,
PRENOM CHAR(30) CCSID 1147 NOT NULL DEFAULT  » )
RCDFMT TESTS

On récupère a nouveau des données

CPYF FROMFILE(TESTA)
TOFILE(TESTN)
MBROPT(*REPLACE) FMTOPT(*NOCHK)

Si on fait un DSPPFM voici le résultat :

Si on fait un select * from TESTS :

conclusion

Si vous copiez des données alpha dans du numérique étendu

1/ En DDS

Les données sont fausses dans le fichier et interprétées à l’exécution SQL
attention si vous lisez en RPG les données ne sont pas chargées et il n’y a pas d’erreur !

exemple :

fTESTN if e disk
read(e) TESTN ;
if %error ;
dsply ‘ici’ ;
else ;
dsply ‘la’ ;
dsply NOM ; ;
endif ;
*inlr = *on ;

2/ En SQL

Le données sont converties lors du CPYF, mais elles ne sont pas bonnes : on passe ici de AAAAA à 11111

Faites attention si vous récupérez des données numériques étendues vous pourriez avoir des surprises

Dernière remarque

Il n’y pas de problème avec des données packées :
En DDS, RPG ou SQL erreur à la lecture.
En SQL, impossible de copier les données.