, , , Surveillez vos requêtes SQL sur votre #IBMi

Il est possible que vous ayez des doutes sur les performances de vos requêtes SQL, voici comment avoir une idée rapide de ce qui tourne

On va utiliser ACS
dans Base de données choisir
SQL Performance center

Vous avez un onglet Affichage des instructions

Vous choisissez le filtre à appliquer, ici on choisit les actives, vous pouvez être beaucoup plus pertinent en limitant votre choix.

Une fois que vous avez repéré une requête candidate, il vous suffit de faire un clic droit sur celle ci et vous pouvez lancer directement Visual Explain, qui vous expliquera le comportement de cette requête, vous pourrez alors faire les ajustements qui s’imposent

Le résultat dans V-E

PS:
Le principal critère de performance sur SQL, c’est les index, pensez à surveiller les suggestions faites par Index Advisor

Une des nouveautés de TR4 c’est Query Supervisor

L’idée est de limiter les requêtes selon certain critères (de temps d’exécution, d’espace temporaire occupé etc… )
Jusqu’à présent c’était pas toujours simple et un peu binaire

Ajout d’un seuil à contrôler

Se fait par la procédure QSYS2.ADD_QUERY_THRESHOLD

Exemple

CALL QSYS2.ADD_QUERY_THRESHOLD(THRESHOLD_NAME => ‘Seuil’,
THRESHOLD_TYPE => ‘CPU TIME’,
THRESHOLD_VALUE => 1,
INCLUDE_USERS => ‘PLB’,
DETECTION_FREQUENCY => 60)

Vous devez indiquer :
– Un nom ici Seuil
– Le seuil à contrôler
– Une valeur pour ce seuil
– Un filtre d’inclusion ou d’exclusion ici inclusion du profil PLB
– Un délai de rafraichissement en seconde

Vous avez une vue qui permet de voir les seuils définis sur votre partition

C’est la vue QUERY_SUPERVISOR

exemple

voir tous les seuils définis pour Query supervisor

SELECT *
FROM QSYS2.QUERY_SUPERVISOR ORDER BY THRESHOLD_TYPE, THRESHOLD_VALUE DESC;

;

Vous pouvez indiquer un programme d’exit QIBM_QQQ_QRY_SUPER

ci joint un exemple basique pour expliquer ce qui ce passe

pgm parm(&qrysupdta &returncod)
/* Paramètres */
dcl &qrysupdta *char 1024
dcl &returncod char 8

/* Variables de Travail */

DCL VAR(&SIZ_HEADER) TYPE(CHAR) LEN(4) +
STG(DEFINED) DEFVAR(&qrysupdta 1)

DCL VAR(&FMT_NAME) TYPE(CHAR) LEN(8) +
STG(DEFINED) DEFVAR(&qrysupdta 5)

DCL VAR(&JOB_NAME) TYPE(CHAR) LEN(10) +
STG(DEFINED) DEFVAR(&qrysupdta 13)

DCL VAR(&JOB_USER) TYPE(CHAR) LEN(10) +
STG(DEFINED) DEFVAR(&qrysupdta 23)

DCL VAR(&JOB_NUMBER) TYPE(CHAR) LEN(6) +
STG(DEFINED) DEFVAR(&qrysupdta 33)

DCL VAR(&SUBSYSTEM) TYPE(CHAR) LEN(10) +
STG(DEFINED) DEFVAR(&qrysupdta 39)

DCL VAR(&Usr_name) TYPE(CHAR) LEN(49) +
STG(DEFINED) DEFVAR(&qrysupdta 23)

DCL VAR(&QRYPLANID) TYPE(CHAR) LEN(08) +
STG(DEFINED) DEFVAR(&qrysupdta 67)

DCL VAR(&THR_NAME) TYPE(CHAR) LEN(60) +
STG(*DEFINED) DEFVAR(&qrysupdta 75)
dcl &msg *char 2056
chgvar &msg (&JOB_NAME *cat ‘/’ *cat &JOB_USER *tcat ‘/’ *tcat +
&JOB_NUMBER *bcat ‘Arreté pour dépassement, ‘ *bcat &THR_NAME)
SNDUSRMSG MSG(&MSG)
/* on force l’arret de la requete */
CHGVAR VAR(%BIN(&returncod 1 4)) VALUE(1)
ENDPGM
Pour en savoir plus le lien ici

https://www.ibm.com/docs/en/i/7.4?topic=ssw_ibm_i_74/apis/xqrysuper.htm

Le message ne cas de débordement

Le message remonté depuis notre programme d’exit

Vous pouvez voir vos statistiques de sollicitation dans ACS SQL performance center

Vous pouvez enlever vos seuils

c’est la procédure REMOVE_QUERY_THRESHOLD

CALL QSYS2.REMOVE_QUERY_THRESHOLD(THRESHOLD_NAME => ‘Seuil’);

vous indiquez le nom que vous avez donné à votre seuil

Conclusion

C’est une bonne nouveauté pour les administrateurs DB2

On peut regretter l’absence du procédure de change qui permettrait de revenir sur les paramètres de définition.

, , UTILISATION DES API EN SQL

Récupérer une API

Il existe un grand nombre d’API aux fonctionnalités diverses dont certaines nous permettent de récupérer des données structurées dans différents formats (XML, JSON, …).

Grace aux fonctions SQL de l’IBMi nous pouvons récupérer ces données pour les insérer dans les fichiers de la base de données.

Pour les exemples qui suivent, on se base sur trois API tirées du site https://openweathermap.org/ :

  • Une première qui récupère la météo dans une ville donnée

https://api.openweathermap.org/data/2.5/weather?q={city name}&appid={API key}&mode=xml’

  • Une qui récupère jusqu’à 50 communes autour de coordonnées choisies

https://api.openweathermap.org/data/2.5/find?lat=45.75&lon=4.5833&cnt=50&appid={API key}&mode=xml

  • Une qui récupère jusqu’à des communes dans un rectangle de coordonnées choisies

https:// api.openweathermap.org/data/2.5/box/city?bbox=4,45,8,46,50&appid={API key}

Extraire les données de l’API

Sortie API en XML

La commande SQL suivante permet d’afficher les données dans un champ DATA 

SELECT DATA FROM (values
char(SYSTOOLS.HTTPGETCLOB('https://api.openweathermap.org/data/2.5/weather?q={city name}&appid={API key}&mode=xml',''), 4096))
ws(data);

Sortie API en JSON

La commande SQL suivante permet d’afficher les données dans un champ DATA 

SELECT DATA FROM (values
char(SYSTOOLS.HTTPGETCLOB('api.openweathermap.org/data/2.5/weather?q={city name}&appid={API key}',''), 4096))
ws(data);

Sortie API en HTML

La commande SQL suivante permet d’afficher les données dans un champ DATA 

SELECT DATA FROM (values
char(SYSTOOLS.HTTPGETCLOB('https://api.openweathermap.org/data/2.5/weather?q={city name}&appid={API key}&mode=html',''), 4096))
ws(data);

Récupération des données

En XML

On crée un fichier qui contiendra les colonnes que l’on veut récupérer (Ville, Température en cours, date, …)

CREATE TABLE GG/METEODB
(VILLE_ID DECIMAL (9, 0) NOT NULL WITH DEFAULT,
VILLE_NOM CHAR (50) NOT NULL WITH DEFAULT,
TEMPERATURE DECIMAL (5, 2) NOT NULL WITH DEFAULT,
TEMP_MIN DECIMAL (5, 2) NOT NULL WITH DEFAULT,
TEMP_MAX DECIMAL (5, 2) NOT NULL WITH DEFAULT,
DATE_MAJ CHAR (20) NOT NULL WITH DEFAULT)
;

Récupérer les données de l’API dans le fichier créé :

INSERT INTO GG.METEODB
select xdata.* FROM xmltable('$doc/cities/list/item'
PASSING XMLPARSE(document SYSTOOLS.HTTPGETCLOB('https://api.openweathermap.org/data/2.5/find?lat=45.75&lon=4.5833&cnt=10&appid={API key}&mode=xml','')) AS "doc"
COLUMNS
ville_id decimal(9, 0) PATH 'city/@id',
ville_nom varchar(50) PATH 'city/@name',
temperature decimal(5, 2) PATH 'temperature/@value',
temp_min decimal(5, 2) PATH 'temperature/@min',
temp_max decimal(5, 2) PATH 'temperature/@max',
date_maj varchar(20) PATH 'lastupdate/@value' ) as xdata;

En JSON

Contrairement à XML, on peut créer tout de suite un fichier qui contiendra les colonnes que l’on veut récupérer.

CREATE TABLE GG.METEOBD
(VILLE_ID DECIMAL (9, 0) NOT NULL WITH DEFAULT,
VILLE_NOM CHAR (50) NOT NULL WITH DEFAULT,
TEMPERATURE DECIMAL (5, 2) NOT NULL WITH DEFAULT,
TEMP_MIN DECIMAL (5, 2) NOT NULL WITH DEFAULT,
TEMP_MAX DECIMAL (5, 2) NOT NULL WITH DEFAULT,
DATE_UX_MAJ DECIMAL (12, 0) NOT NULL WITH DEFAULT)

Récupérer les données de l’API dans le fichier créé :

INSERT INTO GG.METEOBD
select * from JSON_TABLE(SYSTOOLS.HTTPGETCLOB('https://api.openweathermap.org/data/2.5/box/city?bbox=4,45,8,46,50&appid={API key}','') ,
'$.list[*]'
COLUMNS
(ville_id decimal(9, 0) PATH '$.id',
ville_nom varchar(50) PATH '$.name',
temperature decimal(5, 2) PATH '$.main.temp',
temp_min decimal(5, 2) PATH '$.main.temp_min',
temp_max decimal(5, 2) PATH '$.main.temp_max',
date_ux_maj decimal(12, 0) PATH '$.dt'));

Pour aller plus loin

En utilisant une API de LA POSTE qui ne nécessite pas d’inscription au préalable, ni d’identification. Nous pouvons réaliser un programme qui nous aide à retrouver une commune à partir d’un code postal, dans l’optique d’aider au remplissage de certains formulaires.
On crée un fichier temporaire en interrogeant directement l’API.

, , Variables SQL utilisables dans vos scripts ?

Voici quelques variables que vous pouvez utiliser dans vos requêtes SQL

par exemple Client_Ipaddr qui est arrivée avec la TR4

Quelques registres et variables d’environnement

select
current time as heure_en_cours,
current date as date_en_cours,
current user as utilisateur_courant,
current timestamp as timestamp_en_cours,
CURRENT CLIENT_ACCTNG as Client_connexion,
CURRENT CLIENT_APPLNAME as Client_Application,
Current timezone as Fuseau_Horaire,
Current server as Current_Server ,
current path as Current_path,
CURRENT CLIENT_APPLNAME as Programme_client,
CURRENT CLIENT_USERID as Utilisateur_client,
CURRENT CLIENT_PROGRAMID as Programme_client
from sysibm.sysdummy1

Quelques Variables globales dans SYSIBM

select
SYSIBM.Client_Host as Client_Host,
SYSIBM.Client_Ipaddr as Client_Ipaddr,
SYSIBM.Client_Port as Client_Port,
SYSIBM.Package_Name as Package_Name,
SYSIBM.Package_Schema as Package_Schema,
SYSIBM.Package_Version as Package_Version,
SYSIBM.Routine_Schema as Routine_Schema,
SYSIBM.Routine_Specific_Name as Routine_Specific_Name,
SYSIBM.Routine_Type as Routine_Type
from sysibm.sysdummy1

Comme dans nos exemples, ces variables sont utilisables dans vos requêtes

exemple

les travaux actifs de l’utilisateur en cous

, , Passage SQLSTATE en SQL

Depuis la TR4 de la V7R4, vous pouvez passer votre propre SQLSTATE ce qui est très intéressant sur les triggers avants par exemple.
Vous pouvez avoir la vraie raison du refus
C’est un petit pas pour SQL, mais un grand pas pour le développeur SQL

Exemple

Création de la table

CREATE OR REPLACE TABLE ARTICLE (
NOMART CHAR(30) CCSID 297 NOT NULL DEFAULT  » ,
NUMART DECIMAL(6) ,
DESIGN CHAR(25) CCSID 297 NOT NULL DEFAULT  » ,
PRXUNI DECIMAL(6)
)
Insertion d’un article (Je sais, je surfe sur l’actualité du moment)
INSERT INTO ARTICLE VALUES(‘Maillot Benzema’, 19,
‘Maillot Benzema EDF’, 166)

tentative de mise à jour

UPDATE ARTICLE SET PRXUNI = 167 WHERE NUMART = 19

Création d’un trigger de controle
ici on teste que l’utilisateur est bien ‘DBADMIN’ pour pouvoir modifier le tarif

CREATE OR REPLACE TRIGGER ARTICLETRG
BEFORE UPDATE OF PRXUNI ON ARTICLE
FOR EACH ROW
BEGIN ATOMIC
IF CURRENT USER <> ‘DBADMIN’ THEN
SIGNAL SQLSTATE ‘DB999’
SET MESSAGE_TEXT = ‘UTILISATEUR NON AUTORISÉ’;
END IF;
END;

Par SQL

Maintenant dans un programme RPGLE

On va utiliser get diagnostics pour récupérer le SQLTATES et le Message associé

**free
dcl-s MessageText char(45) ;
dcl-s ReturnedSQLState char(5);
exec sql SET OPTION COMMIT = *NONE ;
// test mise à jour trigger
exec sql
UPDATE ARTICLE SET PRXUNI = 167 WHERE NUMART = 19 ;
exec sql
get diagnostics condition 2
:ReturnedSQLState = RETURNED_SQLSTATE ,
:MessageText = MESSAGE_TEXT;
dsply (ReturnedSQLState + ‘ ‘ + MessageText) ;
*inlr = *on ;

Vous lancez le programme

Conclusion :

c’est une nouveauté qui devrait simplifier le contrôle des triggers est donc leur usage.