, Suppression des conseils d’index dans la table SYSIXADV

La table QSYS2.SYSIXADV implémente les conseils de création d’index. Ceci à chaque fois qu’une requête exécutée nécessiterait la création d’un index pour être plus performante. Il est utile de consulter cette table régulièrement afin de créer les index conseillés parmi les plus consommateurs et/ou les plus sollicités.

Il existe la procédure RESET_TABLE_INDEX_STATISTICS (https://www.ibm.com/docs/en/i/7.4?topic=services-reset-table-index-statistics-procedure) dont le premier rôle est de remettre à zéro les statistiques sur ces index. Une option permet la suppression des suggestions à condition que l’index ait été créé entretemps.

Pour réaliser la suppression de ces suggestions, on peut alors se créer une procédure SQL pour réaliser simplement ce ménage de façon régulière une fois que les conseils ont été suivis :
CREATE OR REPLACE PROCEDURE GREFER.DLTADVIDX (
IN LIB CHARACTER(10),
IN FIC CHARACTER(10),
IN JOURS INTEGER)

LANGUAGE SQL
MODIFIES SQL DATA
PROGRAM TYPE MAIN
CONCURRENT ACCESS RESOLUTION DEFAULT
DYNAMIC RESULT SETS 0
OLD SAVEPOINT LEVEL
COMMIT ON RETURN NO
SET OPTION DBGVIEW = *SOURCE
BEGIN
DECLARE SQLCODE INTEGER DEFAULT 0;
DECLARE RETCODE INTEGER DEFAULT 0;
DECLARE SQLSTATE CHAR(5) DEFAULT ‘00000’;
DECLARE RETSTATE CHAR(5) DEFAULT ‘00000’;
DECLARE COMPTE INTEGER;
DECLARE RFIC CHAR(10);
DECLARE OTYPE CHAR(10);
DECLARE MSG CHAR(256);
DECLARE CONTINUE HANDLER FOR SQLEXCEPTION, NOT FOUND SET RETCODE = SQLCODE;
SET RETSTATE = SQLSTATE;
SELECT X.OBJATTRIBUTE INTO OTYPE FROM TABLE (QSYS2.OBJECT_STATISTICS(‘QSYS’, ‘LIB’, LIB) ) AS X ;
IF RETCODE = 0 THEN If UPPER(FIC) = ‘*ALL’ Then
Set RFIC = ‘ ‘;
End If;
If LOCATE(‘*’, FIC) > 1 Then Set RFIC = SUBSTR(FIC, 1, LOCATE(‘*’, FIC) -1);
End If;
If RFIC is not null then
SELECT COUNT(*) INTO COMPTE FROM QSYS2.SYSIXADV WHERE TABLE_SCHEMA=UPPER(LIB) AND TABLE_NAME LIKE UPPER(trim(RFIC))||’%’ AND LAST_ADVISED<(NOW()-JOURS DAYS); Else SELECT COUNT(*) INTO COMPTE FROM QSYS2.SYSIXADV WHERE TABLE_SCHEMA=UPPER(LIB) AND TABLE_NAME = UPPER(FIC) AND LAST_ADVISED < (NOW() - JOURS DAYS); End If; IF RETCODE <> 0 THEN
Set MSG = ‘Anomalie de traitement’;
Return;
END IF;
CASE COMPTE
WHEN 0 THEN
Set MSG = ‘Rien a supprimer’;
Return;
WHEN 1 THEN
Set MSG = TRIM(CHAR(COMPTE)) CONCAT ‘ information supprimee’;
ELSE
SET MSG = TRIM(CHAR(COMPTE)) CONCAT ‘ informations supprimees’;
END CASE;
If RFIC is not null then
DELETE FROM QSYS2.SYSIXADV WHERE TABLE_SCHEMA = UPPER(LIB)
AND TABLE_NAME LIKE UPPER(trim(RFIC))||’%’
AND LAST_ADVISED<(NOW()-JOURS DAYS); Else DELETE FROM QSYS2.SYSIXADV WHERE TABLE_SCHEMA = UPPER(LIB) AND TABLE_NAME = UPPER(FIC) AND LAST_ADVISED<(NOW()-JOURS DAYS); End If; IF RETCODE <> 0 THEN
Set MSG = ‘Anomalie de traitement’;
END IF;
Call systools.lprintf(msg);
ELSE
Set MSG = ‘Bibliothèque inexistante’;
Call systools.lprintf(msg);
END IF;
END;

LABEL ON ROUTINE GREFER.DLTADVIDX(CHAR(), CHAR(), INT)
IS ‘INIT STATS INDEX. (DELETE STATS)’;
COMMENT ON PARAMETER ROUTINE GREFER.DLTADVIDX (CHAR(), CHAR(), INT)
(LIB IS ‘BIBLIOTHÈQUE DU FICHIER’, FIC IS ‘INDEX’, JOURS
IS ‘NOMBRE DE JOURS DERNIÈRE INFORMATION’);

Cette fonction admet trois paramètres, Bibliothèque, Fichier et Nombre de jour de conservation de la suggestion.

Exemples d’utilisation :

Call Grefer.DLTADVIDX(‘MYLIB’, ‘*ALL’, 1); supprimera tous les conseils de plus d’un jour pour tous les fichiers de la bibliothèque MYLIB

Call Grefer.DLTADVIDX(‘MYLIB’, ‘MYF*’, 7); supprimera tous les conseils de plus de 7 jours pour tous les fichiers de la bibliothèque MYLIB dont le nom commence par MYF

Call Grefer.DLTADVIDX(‘MYLIB’, ‘MYFILE’, 28); supprimera tous les conseils de plus de 28 jours pour le fichier MYFILE de la bibliothèque MYLIB

Cela permet de nettoyer ce cumul de conseils sur certaines bibliothèques, fichiers avant d’évaluer les réels besoins de la base de données en se basant sur les ESTIMATED_CREATION_TIME les plus gourmandes et/ou les TIMES_ADVISED les plus nombreuses.

, , Suggestion d’Index agrégés

Vous connaissez index advisor, c’est une table que le système met à jour à chaque suggestion d’index, elle se nomme SYSIXADV et elle est dans QSYS2.

Vous pouvez l’interroger par SQL en faisant un simple select et en appliquant un filtre par rapport à une date de dernière utilisation et soit un nombre de fois recommandés ou un temps de reconstruction.

Exemple :

Depuis 1 mois et plus de 1000 fois

select * from qsys2.SYSIXADV where

LAST_ADVISED > current date – 1 month and times_advised > 1000

Depuis 1 jour et temps de reconstruction > 100 pour la bibliothèque GREFER

select * from qsys2.SYSIXADV where

TABLE_schema = ‘GREFER’ and LAST_ADVISED > current date – 1 days and ESTIMATED_CREATION_TIME > 100

Index agrégé

Il existe une vue sur cette table pour les index agrégés (clés composées) son nom est CONDENSEDINDEXADVICE et son nom court CONDIDXA.

Elle est souvent utilisée dans les outils comme gestion des schémas ou Visual Explain par exemple.

Mais vous pouvez l’utiliser directement dans vos requêtes comme SYSIXADV

Exemple :

Les index suggérés sur la bibliothèque GREFER

select * from qsys2.condidxa where TABLE_schema = ‘GREFER’

Ça vous évitera de créer des index dont les zones sont utilisées dans les autres.

Exemple

Pour la table LSTOBJ

Sur la table SYSIXADV :

Sur la vue CONDIDXA :

dans cet exemple à base d’un dspobjd on voit bien qu’un seul index est suffisant alors qu’ Advisor en suggère 3 de base.

rappel

l’index est un des principaux facteurs de performances de votre base de données , il faut surveiller ce que dit index advisor

, 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/

, , 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.

, , 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.

, Travail à démarrage automatique

Un job à démarrage automatique, c’est un travail qui démarre en même temps que votre sous-système, soit pour initialiser quelque chose, soit pour se mettre en attente d’événements à traiter.

Vous en connaissez au moins 2

Le QSTRUP

C’est le programme indiqué dans la valeur système QSTRUPPGM et il est lancé par votre sous système de contrôle, le plus souvent QCTL .

et le second c’est QSTRRMTWTR


C’est Le démarrage des imprimantes qui est lancé en même temps que QSPL !

Vous pouvez créer vos propres travaux à démarrage automatique

Si vous devez créer un job à démarrage automatique pour vous faciliter l’administration de notre partition, voici les étapes à suivre

Création du sous système (s’il n’existe pas encore)

CRTSBSD SBSD(GAIA/GAIASBS) POOLS((1 *BASE)) TEXT(‘Sous systéme …’)

Ajouter une donnée de routage pour faire

Si elle n’existe pas le plus simple une seule, elle permettra de faire exécuter les jobs qui rentreront votre sous système

ADDRTGE SBSD(GAIA/GAIASBS)
SEQNBR(9999)
CMPVAL(*NONE)
PGM(QCMD)
CLS(QBATCH)

Vous avez intérêt à créer une jobq spécifique avec une entrée dédiée à votre job auto

CRTJOBQ JOBQ(GAIA/AUTO)

ADDJOBQE SBSD(GAIA/GAIASBS)
JOBQ(GAIA/AUTO)
MAXACT(1)

Vous pouvez créer une autre jobq qui permettra à d’autres travaux de s’exécuter soit les uns à la suite des autres

ADDJOBQE SBSD(GAIA/GAIASBS)
JOBQ(GAIA/GAIA)
MAXACT(1)

Ou tous ensemble

ADDJOBQE SBSD(GAIA/GAIASBS)
JOBQ(GAIA/GAIA)
MAXACT(*NOMAX)

Vous devez ensuite créer une jobd

CRTJOBD JOBD(GAIA/AUTO)
JOBQ(GAIA/AUTO)
TEXT(‘File pour job auto …’)
USER(QPGMR)
RTGDTA(‘QCMDB’)
RQSDTA(‘CALL GAIA/PGMAUTO’)

Cette jobd doit comporter 2 particularités , un utilisateur, paramètre USER ici QPGMR et un programme à lancer paramètre RQSDTA ici appel du programme PGMAUTO.

Vous pouvez maintenant ajouter votre travail à démarrage AUTO

ADDAJE SBSD(GAIA/GAIASBS)
JOB(AUTOJOB)
JOBD(GAIA/AUTO)

Quand vous ferez un STRSBS GAIA/GAIASBS votre job AUTOJOB sera exécuté.

Attention :

Les programmes à démarrage automatique ne doivent pas planter, pensez y également quand vous adaptez le programme QSTRUP

Vous voulez avoir des informations sur des exécutions SQL.

La meilleure méthode est de prendre la requête que vous voulez analyser et de l’exécuter dans Visual Explain.

Mais ce n’est pas toujours possible , dans le cas d’une chaine avec du SQL embarqué .

Si vous avez fait du SQL statique vous pouvez avoir des informations au niveau des programmes par PRTSQLINF.

Dans certains cas, on n’a pas ces possibilités. Le plus simple est alors de ce mettre en debug et d’exécuter votre chaine, attention ce n’est pas un mode à mettre en place en production, ça augmente considérablement les informations de log.

Vous devez démarrer le debug, le plus souvent en indiquant que vous autorisez les mises à jour sur les fichiers de production.
==>strdbg updprod(*yes)

Vous lancez ensuite votre traitement qui va générer un log avec des messages de votre optimiseur SQL, ce sont des messages id CPI43xx

Il vous suffit ensuite d’analyser votre log et regarder si certains messages vous donnent des informations intéressantes.
exemple :
CPI432F index à ajouter

Pour vous aider nous avons fait cet outil DBGSQLJOB que vous retrouverez ici et qui vous facilite la démarche
https://github.com/Plberthoin/PLB/tree/master/GTOOLS

Vous soumettez votre programme comme ceci
sbmjob cmd(DBGSQLJOB cmd(call monpgm) ) job(dbgsql)

et vous obtenez un fichier dbgsql dans la bibliothèque indiquée (par défaut *curlib) qui contiendra les messages de votre optimiseur.

Vous n’avez plus qu’a les analyser ensuite par select * from DBGSQL .

Limitation :
Dans cet outil, nous traitons en commit(*none) les requêtes SQL, si ce n’est pas le cas chez vous, adaptez le mode.

Rappel :
80 % de votre performance SQL c’est les index , suivez les recommandations de INDEX ADVISOR.