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