PERFORMANCES S Q L -------------------- Normalisation de la base : - respectez les formes normales pour une base de production .......................................................................... : toute colonne d'une table dépend directement et uniquement de la clé ! : :........................................................................: - mettez en place l'intégrité référentielle (cela créé des indexs) - ajoutez des contraintes CHECK (l'optimiseur en tient compte) - évitez les clefs composites préférez les clefs purement informatiques, voir les champs IDENTITY - évitez les colonnes "nullables" en tant que clef, réservez cette notion aux données ou c'est important (date de départ, prix, ...) |
- limitez les zones à taille variable (CHAR / VARCHAR) accèdez à une zone VARCHAR sous DB2/400, c'est deux I/O disque ! - Ayez un dictionnaire de données afin qu'une information soit toujours codée de manière identique A EVITER ABSOLUMENT : - le n° de client packé dans un fichier, étendu dans l'autre - date dans un fichier et jour/mois/année dans l'autre - ... "mapper" une donnée (convertir) c'est toujours ne pouvoir utiliser aucun des index existant. - n'indexez que l'essentiel, un index c'est aussi des cycles CPU consommées lors des écritures et des mises à jour. |
Réutilisation des ODP (résultat d'un OPEN) et des index. SQL laisse ouvert les ODP qu'il a créé afin de les réutiliser. DANS un PGM l'ODP ne sera ré-utilisé qu'avec le même n° d'instruction (==> Appel de pgm ou sous-pgm) La commande système RCLRSC ferme les ODP crées par SQL. SQL utilise des index d'accès pour les clauses WHERE GROUP BY / ORDER BY S'ils existent, SQL les utilise, sinon il peut être ammené à les créer. (d'où l'interêt de créer des index définitifs. CREATE INDEX ou LF) Un index ne peut jamais être explicitement utilisé, c'est l'optimiseur (le moteur) qui décide en fonction, aussi, du contexte. dans certains cas un balayage complet du fichier sera moins pénalisant (en fonction du nombre de lignes et de la taille mémoire de votre AS) |
SELECTION il faut bien voir que le but est de gagner du temps. Or ce qui prend le de temps, ce sont le E/S (I/O) disque. SQL va donc, TOUJOURS, chercher à optimiser la sélection. ce qui semble le plus évident, c'est l'utilisation d'index. Mais, l'utilisation d'un index pour sélectionner les enregistrements, va ensuite pénaliser les E/S pour remonter les lignes en mémoire. En effet, si les postes d'index sont contigus, les enregistrements ne le sont pas dans le fichier, du moins, pas dans l'ordre de l'index. ............ ....................................... : A : 003 : 1 : Vis en bois : 45 : 12,34 : C : : B : 006 : 2 : clou en papier: 122 : 33,44 : F : : C : 001 : 3 : trombonne : 2 : 55,34 : A : : D : 005 : 4 : règle en fer : 15 : 12,45 : E : : E : 004 : 5 : tournevisse : 55 : 22,22 : D : : F : 002 : 6 : clé à pipe : 92 : 99,00 : B : :..........: :.....................................: |
Sur l'exemple qui précède, si'il faut prendre les enregistrements ayant la famille comprise entre C et D. il faudra aller chercher la ligne 1 puis la ligne 5. Donc 2 I/O (peut-être trop éloignées pour une seule). S'il faut prendre tout ce qui est entre A et E, mieux vaudrait charger tout le fichier en mémoire et trier "manuellement". SQL va donc estimer le nombre de lignes correspondant à votre sélection. 1/ en allant voir les infos tenues à jour par DB2/400 (V5R10) résultat de DSPFD sur un index classant les vins (25.221) par cépage. .......................................................................... : Taille de l'index . . . . . . . . . . . : 598016 : : Validité du chemin d'accès . . . . . . : Oui : : Partage de chemin d'accès implicite . . : Non : : Chemin d'accès journalisé . . . . . . . : Non : : Nombre val clés partielles uniques . . : : : Zone clé 1 . . . . . . . . . . . . . : 232 : :........................................................................: ce qu'affiche aussi : select count(distinct vin_cepage1) from vins |
2/ En réalisant des sondages dans les index, sur la première clé pour connaitre le nombre de valeurs par clé et en estimant le nombre de lignes correspondant à vos critères (voir les statistiques en V5.2) Il est possible que DB2 utilise l'index pour réaliser ces sondages et ne l'utilise pas pendant sa requête. Il aura quand même été utile ! l'option de QAQQINI:OPTIMIZE_STATISTIC_LIMITATION peut limiter ces sondages --------------------------------------------------------------------------- L'intéret de ne pas utiliser d'index est dépendant de votre sous-système disque et votre mémoire principale. JOINTURE Pour réaliser la jointure, DB2/400 utilise la plupart du temps une technique nommée "nested loop join" ou boucle imbriquée. il s'agit pour un n° de client, d'utiliser l'index classant les commandes par client, et ce pour chaque client (positionnement, lecture tant que...) ces index existent TOUJOURS avec l'intégrité référentielle ! |
l'idéal étant de trouver un index portant sur la sélection ET le critère de jointure. par exemple : SELECT * FROM CLIENTS JOIN CDES on CLIENTS.NOCLI = CDES.NOCLI WHERE DEPCLI = 44 si DB2 trouve un index sur NOCLI ET DEPCLI, il sera forcement utilisé. (jointure cdes vers clients, sauf LEFT JOIN ...) l'OPTION "FORCE_JOIN_ORDER" dans QAQQINI permet d'interdir à DB2/400 de ré-agencer l'ordre des fichiers dans la clause FROM. GROUPAGE s'il y a un petit nombre de lignes par GROUPE, DB2 va privilégier un index sinon, il va privilégier le Haschage. (voir plus loin) TRI pour le tri, un index ou une routine de tri (SORT) va être utilisée. |
SQL n'utilise pas d'index dans les cas suivants: - Plus de 20% des lignes correspondent à la sélection (clause WHERE) - Comparaison entre deux arguments de type différent(CAST par exemple) .................................................................... :la requête suivante empèche l'utilisation d'un index sur fichier2 : : pour la jointure(il ne peut exister d'index année,mois,jour) : : : : fichier1 JOIN fichier2 on AA = SUBSTR(DATE, 5 , 4) : : and MM = SUBSTR(DATE, 3 , 2) : : and JJ = SUBSTR(DATE, 1 , 2) : : : :la requête ci-dessous utilise un index sur "DATE" : : : : fichier1 JOIN fichier2 on (AA CONCAT MM CONCAT JJ) = DATE : :..................................................................: - Comparaison entre une variable alpha et un argument plus grand que la variable. - Clause LIKE commencant par '%' |
- Clause LIKE avec une variable HOST dans un pgm RPG ou COBOL. (dans ce cas il est plus intéressant d'utiliser SQL dynamique avec PREPARE et EXECUTE) - Comparaison de deux colonnes de la même ligne. - éviter les index sur un champs VARCHAR(), ils sont mal gérés par DB2 (particulièrement, les statistiques vues sur DSPFD) - éviter la valeur nulle admise, sur les clés importantes. (c'est impossible avec les PRIMARY KEY de toutes facons) Privilégiez la jointure avec JOIN (plus souple, plus lisible) Préférez EXISTS (traité en V5R10 comme une jointure) quand c'est possible : remplacez where (select count(*) from fichier where ....) = 0 par where NOT EXISTS (select * from fichier T02 where T02.clé=T01.clé) |
Si vous souhaitez ignorer la casse (différence minuscules/majuscules) lors de vos recherches : 1/ utiliser UCASE, par exemple : Select * from VINS where ucase(cepage) like 'CABERNET%' ->le problème c'est que SQL ne peut réutiliser alors, aucun index existant avant la version 6. 2/ Utiliser un critère de tri particulier (paramètre SRTSEQ) : *LANGIDSHR ->si vous avez créé un index avec les mêmes attributs, il est utilisé ! avec SDD, vous utilisez SRTSEQ( ) et LANGID( ) sur la commande CRTLF avec SQL, indiquez cette propriété pour la session SQL : STRSQL SRTSEQ(*LANGIDSHR) LANGID(FRA) sous ODBC/JDBC, onglet "Langue" - Tri par ID Langue - Poids partagé |
Version 6 A/ On admet les expressions en tant que clé CREATE INDEX i1 on table T1 ( UPPER(NOM) as NOMMAJ ) la zone NOMMAJ est la clé de cet index. toute requête utilisant WHERE UPPER(NOM) like '...%', utilisera implicitement l'index en question à partir de la version7. l'expression ne peut pas contenir : - des sous requêtes - des fonctions agrégées (COUNT, AVG, SUM, etc ...) - des fonctions NOT DETERMINISTIC (dont le résultat varie), comme NOW() - des UDF sauf celles liées à un nouveau type de données (UDT) - la manipulation de SEQUENCE - des variables , des marqueurs ("?") |
B/ On admet la clause WHERE sur les index : CREATE INDEX i1 on table T1 (NOM) WHERE NOM NOT LIKE 'VOL%' la clause WHERE subit les mêmes règles que les expressions C/ Vous pouvez préciser un format par la clause RCDFMT suivi de la phrase suivante : ADD ALL COLUMNS : toutes les colonnes du PF appartiennent au format ADD KEYS ONLY : seules les zones clés appartiennent au format ADD col1, col2 : ces zones font suite aux zones clés dans le format Exemple récapitulatif : CREATE INDEX logi1 on HTTPLOG ( SUBSTR(virtualhost, 1 , 10) VHOST2 ) Where virtualhost IS NOT NULL RCDFMT httpfmt2 ADD host, logtime -- en plus de VHOST2 |
Certains SGBD utilisent la technique des index bitmap soit la table suivante : l'index bitmap associé sera ##################### rang # client ville # valeur bitmap ##################### ...................... 1 # 1 NANTES # : NANTES : 10000100 : 2 # 6 ANGERS # : ANGERS : 01000000 : 3 # 3 RENNES # : RENNES : 00100010 : 4 # 7 CHOLET # : CHOLET : 00010000 : 5 # 8 BREST # : BREST : 00001000 : 6 # 5 NANTES # : VANNES : 00000001 : 7 # 4 RENNES # :........:...........: 8 # 2 VANNES # 12345678 # # ##################### On place un "1" pour chaque enregistrement ayant cette clé à la position du n° de rang pour "NANTES", les enregistrements 1 et 6. |
DB2/400 n'utilise pas les index bitmap, par contre la technologie bitmap est utilisée pour gérer vos requêtes avec des tests. (depuis la V4R20) Il utilise un index pour chaque critère, construit un nuage de points (bitmap) sur chaque résultat, et applique une fonction boolèenne AND/OR. le résultat indique par un "1" la liste des enregistrements ayant un critère (OR) ou vos critères (AND) vrai(s). NANTES ou ANGERS fournit 10000100 puis 01000000 11000100 ==> les lignes 1,2 et 6. l'AS/400 est capable d'utiliser une technique de haschage pour les jointure. Cela consiste à fabriquer une table temporaire ou la position de l'enregistrement est dépendante de sa valeur de clé (hash-coding) Pour ces deux raisons, il est conseillé d'indiquer ALWCPYDTA(*OPTIMIZE) qui autorise la copie des données, dans des fichiers temporaires. (ATTENTION, sous STRSQL ajoutez réaffichage *FORWARD ) |
EVI enfin, qui est un concept Rochester (il y a un brevet), l'AS/400 étant le premier à l'utiliser. EVI est une utilisation avancèe des index Bitmap Vecteur -------------------------- +------+ !rang client ville ! ........................... | code | !------------------------! : Table des symboles : +------+ !1 ! 1 ! NANTES ! :.........................: | 1 | 1 !2 ! 6 ! ANGERS ! :Valeur:code:Deb:Fin: nbre: | 2 | 2 !3 ! 3 ! RENNES ! : : : : : : | 3 | 3 !4 ! 7 ! CHOLET ! :NANTES: 1 : 1 : 6 : 2 : | 4 | 4 !5 ! 8 ! BREST ! :ANGERS: 2 : 2 : 2 : 1 : | 5 | 5 !6 ! 5 ! NANTES ! :RENNES: 3 : 3 : 7 : 2 : | 1 | 6 !7 ! 4 ! RENNES ! :CHOLET: 4 : 4 : 4 : 1 : | 3 | 7 !8 ! 2 ! VANNES ! :BREST : 5 : 5 : 5 : 1 : | 6 | 8 ! ! ! ! :VANNES: 6 : 8 : 8 : 1 : +------+ !------------------------! :......:....:...:...:.....: On voit que les lignes 1 et 6 sont de code "1" c'est à dire NANTES. |
Le vecteur contient un poste par enregistrement, la position donnant le n° de rang, MAIS on indique un code et non une valeur de clé. On indique en plus une table des symboles qui contient - la correspondance entre chaque valeur de clé et son code associé - des statistiques destinées au "Query Governor", l'optimiseur de requêtes Il faut simplement indiquer le nombre de valeurs différentes lors de la création afin de savoir s'il faut créér un vecteur sur 1,2 ou 4 octets si vous ne connaissez pas ce nombre de valeurs différentes DB2 va les rechercher pour vous AU MOMENT DE LA CREATION (non réactualisé) . CREATE ENCODED VECTOR INDEX on fichier(clé1, clé2, ...) FOR x DICTINCT VALUES |
> les index EVI ne sont pas utilisables pour : - les jointures - le groupage d'enregistrements - le tri > les index EVI sont à conseiller pour : - un petit nombre de valeurs différentes avec beaucoup de lignes (famille de produit, département, année/semaine , etc...) - des données peu mouvantes (un produit change peu de famille) - des données en fort volume (gros fichiers) > DB2/400 va bien utiliser la technique bitmap sur DEUX index EVI (ou plus) portant chacun sur une SEULE colonne. |
Version 7 - Les index avec sélection d'enregistrement(avec une clause WHERE) sont maintenant pleinement utilisés par SQE - Nouvelle possibilité d'inclure des fonctions agrégées (SUM,AVG,COUNT,VAR,STDDEV) dans un index EVI. CREATE ENCODED VECTOR INDEX EVI01 ON COMMANDES (DATCMD, FAM) INCLUDE (SUM(QTE) , COUNT(*) ) Avec le groupe PTF SF99701 level 18, les requêtes utilisant GROUPING SET, ROLLUP ou CUBE, bénéficient aussi de ce type d'index. |
quelques exemples : SELECT * FROM PRODUITS where COULEUR in ('BLEU', 'VERT' ,'ROUGE') ==> CREATE INDEX PROD_IB01 ON PRODUITS (COULEUR) ou CREATE ENCODED VECTOR INDEX PROD_IE01 ON PRODUITS (COULEUR) SELECT * FROM PRODUITS where COULEUR in ('BLEU', 'VERT' ,'ROUGE') and TAILLE in ('L' , 'XL') ==> CREATE INDEX PROD_IB02 ON PRODUITS (COULEUR, TAILLE) [si possible, la clé la plus sélective en 1er] ou CREATE ENCODED VECTOR INDEX PROD_IE01 ON PRODUITS (COULEUR) CREATE ENCODED VECTOR INDEX PROD_IE02 ON PRODUITS (TAILLE) |
SELECT * FROM PRODUITS where COULEUR in ('BLEU', 'VERT' ,'ROUGE') and TAILLE in ('L' , 'XL') and FIBRE = 'COTON' ==> CREATE INDEX PROD_IB03 ON PRODUITS (COULEUR, TAILLE, FIBRE) ou CREATE ENCODED VECTOR INDEX PROD_IE01 ON PRODUITS (COULEUR) CREATE ENCODED VECTOR INDEX PROD_IE02 ON PRODUITS (TAILLE) CREATE ENCODED VECTOR INDEX PROD_IE03 ON PRODUITS (FIBRE) SELECT * FROM PRODUITS where COULEUR in ('BLEU', 'VERT' ,'ROUGE') and TAILLE in ('L' , 'XL') and FIBRE = 'COTON' AND DISPO > 500 ==> CREATE INDEX PROD_IB04 ON PRODUITS (COULEUR, TAILLE, FIBRE,DISPO) [la colonne testée par ">", doit être la dernière ! ] ou idem pour les index EVI [plus un pour "DISPO"] |
SELECT * FROM PRODUITS p JOIN CDES e on p.CODPRO=e.CODPRO JOIN FAMILLES f on p.CODFAM=f.CODFAM where COULEUR in ('BLEU', 'VERT' ,'ROUGE') ==> CREATE INDEX PROD_IB05 ON PRODUITS (COULEUR, CODPRO) CREATE INDEX PROD_IB06 ON PRODUITS (COULEUR, CODFAM) puis (s'ils n'existent pas déja du fait de l'intégrité ref.) CREATE INDEX CDES_IB01 ON CDES (CODPRO) CREATE INDEX FAM_IB01 ON FAMILLES (CODFAM) SELECT COULEUR, SUM(VALEUR), SUM(QTE) FROM PRODUITS where COULEUR in ('BLEU', 'VERT' ,'ROUGE') group by COULEUR, TAILLE ==> CREATE INDEX PROD_IB02 ON PRODUITS (COULEUR, TAILLE) (pas d'evi) |
SELECT COULEUR, TAILLE, MIN(QTE) FROM PRODUITS where COULEUR in ('BLEU', 'VERT' ,'ROUGE') group by COULEUR, TAILLE ==> CREATE INDEX PROD_IB07 ON PRODUITS (COULEUR, TAILLE, QTE) SELECT COULEUR, TAILLE, MAX(QTE) FROM PRODUITS where COULEUR in ('BLEU', 'VERT' ,'ROUGE') group by COULEUR, TAILLE ==> CREATE INDEX PROD_IB08 ON PRODUITS (COULEUR, TAILLE, QTE desc) [desc est inutile avec le nouveau moteur SQE livré en V5R20] SELECT COULEUR, TAILLE, VENTES FROM PRODUITS where VENTES < (Select avg(ventes) From produits Where taille in ('L' , 'XL') ) and COULEUR in ('BLEU', 'VERT' ,'ROUGE') and TAILLE in ('L' , 'XL') ==> CREATE INDEX PROD_IB09 ON PRODUITS (TAILLE, COULEUR) [ici, TAILLE est privilégiée, car étant commune aux 2 requêtes] |
Bien sur, les index sont aussi utilisés sur les ordres UPDATE, DELETE : UPDATE PRODUIT SET PRIX = (Select MAX(PRIX) From CDES C Where C.CODFAM=p.CODFAM ) WHERE COULEUR in ('BLEU', 'VERT' ,'ROUGE') ==> CREATE INDEX PROD_IB01 ON PRODUITS (COULEUR) et CREATE INDEX CDES_IB02 ON CDES (CODFAM , PRIX desc) DELETE FROM PRODUIT WHERE CODFAM in (12, 27, 456, 998) AND QTE = 0 ==> CREATE INDEX PROD_IB10 ON PRODUITS (CODFAM, QTE) |
Vous pouvez aussi, optimiser SQL en utilisant le groupage d'enregistrements Clause OPTIMIZE FOR sur le SELECT [complétée par OVRDBF SEQONLY( ).] , et choix d'un nombre de lignes sur les ordres FETCH et INSERT. Quelques comparatifs réalisés sur un Iseries POWER-PC (regardez les écarts) Ajouts d'enregistrements : Méthode clause ROW SEQONLY(*YES) Enregistrements par minute ------------ ------------ ------------- -------------------------- SQL (INSERT) sans sans 2 300 SQL sans avec 6 300 SQL oui (100) avec 19 950 RPG (Write) - sans 3 800 RPG - avec 17 750 |
Lecture séquentielle sur la clé : Nombre de Enregistrements Méthode clause ROW SEQONLY(*YES) zones par minute ------------ ------------ ------------- ---------- -------------- SQL (FETCH) sans sans 20 20 700 SQL sans avec 20 22 200 SQL oui (100) avec 20 39 450 SQL oui (100) avec 3 44 100 RPG (Read) - sans 39 200 RPG - avec 42 850 Contrairement à la mauvaise réputation de SQL, il est plus performant que les entrées/sorties natives à condition de limiter le nombre de colonnes extraites. (ces tests ont été faits en V3R60 avec RPG-IV) |
Lecture d'un groupe d'enregistrements avec clé : Nombre de Enregistrements Méthode clause ROW SEQONLY(*YES) zones par minute ------------ ------------ ------------- ---------- -------------- SQL (FETCH) sans sans 20 10 100 SQL sans avec 20 9 750 SQL oui (10) sans 20 11 900 SQL oui (10) avec 20 11 900 SQL oui (10) sans 3 12 750 SQL oui (10) avec 3 12 100 RPG (ReadE) - sans 13 800 RPG - avec 13 650 Ici, c'est RPG le "gagnant", ainsi que sur les ordres de mise à jour : Nombre maxi d'enregistrements mis à jour (par minute) avec UPDATE SQL : 11 100 avec RPG : 14 600 |
Dernier point, SQL en tant que langage de conception de bases (DDL) : 1/ CREATE TABLE un fichier physique créé par SDD contrôle mal les données insérées, il y a donc un contrôle supplémentaire réalisé lors de la lecture. Ce contrôle des données insérées étant mieux fait par SQL, les contrôles lors de la lecture n'ont pas lieu, elles sont donc plus rapides. Certains spécialistes chez IBM, parlent de 40% de gain ! 1/ CREATE INDEX les index créés par SQL contiennent un chemin d'accès basé sur des pages de 64 K,ceux créés par SDD sont au maximum à 32K (Op.nav le montre). ceci est dû à l'utilisation intensive faite pas SQL des indexs sur les grosses tables (décisionnelles particulièrement). Si vous avez de la mémoire disponible (il en faut aujourd'hui pour SQL) cela peut faire une véritable différence. |
Il est toujours possible de connaître les décisions de l'optimiseur de requêtes (méthode choisie, index utilisées, avertissements, ...) En 5250, il suffit d'être en mode DEBUG (STRDBG sans paramètres) SQL va alors "loger" dans l'historique des informations sous forme de messages de type *INFO. Les C.A (index) explorés et les raisons pour lesquelles ils ont été soit retenus, soit écartés, sont indiquées dans le texte de second niveau, et plus encore ... Exemple : ...................................... STRDBG : Nous pouvons demander la même : : chose au gestionnaire de scripts : CALL xxxx ou STRSQL. : SQL sous Iseries Navigator avec : : "Option/inclusion messages de : DSPJOBLOG : debogage dans l'historique" : :....................................: |
Historique du travail Système : -------- Travail : NANTEV01 Utilisateur: xxxxxxxxx Numéro . . . : 158734 > call xxxxxxxx >> dspjoblog F10 ! ! ! ! Fin Appuyez sur ENTREE pour continuer. ! v F3=Exit F5=Réafficher F10=Messages détaillés F12=Annuler F17=Début F18=Fin |
Affichage de tous les messages Système : -------- Travail : NANTEV01 Utilisateur: xxxxxxxxx Numéro . . . : 158734 > call yyyyyy <-- (pgm rpg contenant du SQL) L'instruction PREPARE P1 a été exécutée. Tous les chemins d'accès ont été considérés pour le fichier CLIENTS. Tous les chemins d'accès ont été considérés pour le fichier CDES. Chemin d'accès créé à partir du fichier indexé CLIENTL1. Fichier CLIENTS traité en position de jonction 1. Fichier CDES traité en position de jonction 2. ODP créé. Groupage utilisé pour la requête. Le curseur C1 est activé. Conversion de données nécessaire pour FETCH ou SELECT imbriquée. 1 lignes ont été extraites du curseur C1. La ligne n'a pas été trouvée. A suivre... Appuyez sur ENTREE pour continuer. F3=Exit F5=Réafficher F12=Annuler F17=Début F18=Fin |
+ V3R60/R20 : une nouvelle fonction pour analyser votre activité BdeD. STRDBMON = démarre le moniteur de base de données, pour un job ou pour tous les jobs de la machine. (on indique le nom du fichier de sortie). ENDDBMON = arrêt du moniteur et écriture de l'analyse dans le fichier Chaque requête est analysée en détail avec : - liste des fichiers traités - options utilisées (jonction, groupage, ....) - liste des index examinés (raison du choix ou du refus) - nombre d'enregistrements traités - consommation CPU, ... (le fichier modèle est QAQQDBMN ) |
Les enregistrements sont identifiés par un code (QQRID) QQQ1000 informations SQL QQQ3000 séquence d'arrivée " 3001 utilisation d'un index existant " 3002 création d'index " 3003 tri " 3004 utilisation d'un fichier temporaire " 3005 fichier verrouillé " 3006 Chemin d'accès reconstruit " 3007 timeout de l'optimiseur " 3008 sous-requête (SELECT dans un WHERE) " 3010 utilisation d'une variable HOST " 3014 informations générales " 3018 STRDBMON/ENDDBMON " 3019 détail remarquons les zones suivantes : |
QQRIDX (15,0) nombre d'index créés QQIDXA suggestion d'index QQIDXD " " détail (liste des clés) QQC21 méthode de jointure NL = Nested loop Join MF = idem mais avec sélection HJ = Hash Join QQC12 (QQRID = 3001) l'index tient en mémoire QQC15 ( " " ) type d'index B = B-arbre C = Contrainte E = Evi X = temporaire QVC1B (QQRID = 3002) type d'index créé (B ou E) Pour plus de détails, voyez le chapitre 12 du RedBook : DB2 UDB for iSeries, Database Performance and Query Optimization |