Techniques d'amélioration des performances des requêtes SQL sur les espaces de travail de la couche service de données
- Optimisation globale de READ SQL pour DB2
- Optimisation des requêtes en fonction des pratiques recommandées en matière d'optimisation
- Insertion de prédicats dans la clause OUTER JOIN dès que possible
- Duplication de la condition de constante pour différentes tables dès que possible
- Utilisation de définitions de tables imbriquées pour remplacer des vues d'espace de travail
- Fractionnement des requêtes
Utilisation de la syntaxe d'expression commune dans Oracle
Optimisation globale de READ SQL pour DB2
et Oracle
Les règles semblent donner un meilleur temps de réponse sur DB2.
Optimisation des requêtes en fonction des pratiques recommandées en matière d'optimisation
- Indexez tous les prédicats dans des clauses JOIN, WHERE, ORDER BY et GROUP BY.
HCL Commerce repose généralement en grande partie sur les index pour améliorer les performances SQL et l'évolutivité. Sans index appropriés, les requêtes SQL peuvent entraîner des analyses de table, ce qui impacte les performances ou génère des incidents de verrouillage. Il est recommandé d'indexer toutes les colonnes de prédicats. Seule exception : lorsque les données de colonne possèdent une très faible cardinalité.
- Evitez d'utiliser des fonctions dans les prédicats.L'index n'est pas utilisé par la base de données si la colonne comporte une fonction. Par exemple :
En raison de la présence de la fonction UPPER(), l'index sur COL1 n'est pas utilisé pour les optimiseurs de base de données.SELECT * FROM TABLE1 WHERE UPPER(COL1)='ABC'
S'il n'est pas possible d'éviter la fonction dans le langage SQL, vous devez créer un index basé sur la fonction dans Oracle, ou bien générer des colonnes dans DB2 pour améliorer les performances. - Evitez d'utiliser un caractère générique (%) en début de prédicat.Le prédicat
LIKE '%abc'entraîne une analyse complète de la table. Par exemple :SELECT * FROM TABLE1 WHERE COL1 LIKE '%ABC'Il s'agit d'une limitation des performances connue pour toutes les bases de données.
- Evitez d'inclure des colonnes inutiles dans une clause SELECT.
Indiquez les colonnes dans la clause
SELECTplutôt que d'utiliserSELECT *. Les colonnes inutiles engendrent des charges supplémentaires sur le disque de la base de données, ce qui ralentit non seulement le code SQL mais la totalité du système. - Lorsque cela est possible, préférez une jointure interne à une jointure externe.
La jointure externe doit être utilisée uniquement lorsque cela est nécessaire. Elle limite en effet les options d'optimisation de la base de données, ce qui ralentit généralement l'exécution du code SQL.
- DISTINCT et UNION doivent être utilisés uniquement si nécessaire.
Les opérateurs DISTINCT et UNION entraîne un tri, ce qui ralentit l'exécution du code SQL. Utilisez UNION ALL à la place de UNION, si possible, car cet opérateur est beaucoup plus efficace.
Oracle 10g et 11g nécessite le placement des colonnes CLOB/BLOB à la fin des instructions.Un autre choix entraîne un arrêt anormal si la valeur en entrée dépasse les 1000 caractères.
- La clause ORDER BY est obligatoire dans le code SQL si vous prévoyez un ensemble de résultats triés.
Le mot clé ORDER BY est utilisé pour trier l'ensemble de résultats en fonction des colonnes spécifiées. Sans clause ORDER BY, les résultats sont renvoyés directement sans tri d'aucune sorte. L'ordre n'est pas garanti. Soyez conscient de l'impact sur les performances causé par l'ajout de la clause ORDER BY, la base de données devant effectuer un tri de l'ensemble de résultats, ce qui entraîne l'une des opérations les plus coûteuses en terme d'exécution SQL.
Insertion de prédicats dans la clause OUTER JOIN, si possible
Pour les requêtes SQL avec la jointure externe sur l'élément de gauche (LEFT OUTER JOIN), l'insertion du prédicat de la table de droite à partir de la clause WHERE dans la condition ON permet à l'optimiseur de base de données de générer une requête plus efficace. Les prédicats de la table de gauche peuvent rester dans la clause WHERE.
De façon similaire, pour les requêtes SQL avec la jointure externe sur l'élément de droite (RIGHT OUTER JOIN), les prédicats de la table de droite doivent être déplacés depuis la clause WHERE dans la condition ON.
Par exemple, la requête suboptimale est réécrite en insérant des prédicats applicables à la table TAB_B dans la clause ON. Les prédicats spécifiques à TAB_A dans la clause WHERE peuvent y rester ou être insérés dans la clause ON :
SELECT TAB_A.COL1, TAB_B.COL1 FROM TAB_A LEFT OUTER JOIN TAB_B ON TAB_A.COL3 = TAB_B.COL3 WHERE TAB_A.COL1=123 AND TAB_B.COL2=456;
Instruction SQL optimisée :
SELECT TAB_A.COL1, TAB_B.COL1 FROM TAB_A LEFT OUTER JOIN TAB_B ON TAB_A.COL3 = TAB_B.COL3 AND TAB_B.COL2=456 WHERE TAB_A.COL1=123;
Les prédications des jointures internes (INNER) peuvent demeurer dans la clause WHERE. Si les tables TAB_A et TAB_B sont définies en tant que vues, l'optimiseur peut insérer ces prédicats dans les vues.
Duplication de la condition de constante pour différentes tables, si possible
Lorsque deux tables, A et B, sont jointes et qu'il existe un prédicat constant sur l'une des colonnes jointes, par exemple, A.id=B.id et A.id dans (10, 12), le prédicat constant doit être dupliqué pour la colonne jointe de la seconde table. A savoir, A.id=B.id et A.id dans (10, 12) et B.id dans (10, 12).
Par exemple, TAB_A a une relation LEFT OUTER JOIN (jointure externe gauche) avec TAB_B. S'il existe une condition spécifique à TAB_A et une condition de table croisée avec TAB_B, créez une condition supplémentaire spécifique à TAB_B en fonction de l'exigence de TAB_A et conservez les conditions de table croisée dans la clause ON :
SELECT TAB_A.COL1, TAB_B.COL1 FROM TAB_A LEFT OUTER JOIN TAB_B ON TAB_A.COL3 = TAB_B.COL3 WHERE TAB_A.COL1 IN (123, 456) AND TAB_B.COL2=TAB_A.COL1;
Instruction SQL optimisée :
SELECT TAB_A.COL1, TAB_B.COL1 FROM TAB_A LEFT OUTER JOIN TAB_B ON TAB_A.COL3 = TAB_B.COL3 AND TAB_B.COL2 IN (123, 456) AND TAB_B.COL2=TAB_A.COL1 WHERE TAB_A.COL1 IN (123, 456);
En particulier, si le prédicat de constante comporte uniquement la valeur 1 (par ex. : COL1=123), le deuxième prédicat doit également être converti en prédicat de constante.
Par exemple :
SELECT TAB_A.COL1, TAB_B.COL1 FROM TAB_A LEFT OUTER JOIN TAB_B ON TAB_A.COL3 = TAB_B.COL3 WHERE TAB_A.COL1=123 AND TAB_B.COL2=TAB_A.COL1;
Instruction SQL optimisée :
SELECT TAB_A.COL1, TAB_B.COL1 FROM TAB_A LEFT OUTER JOIN TAB_B ON TAB_A.COL3 = TAB_B.COL3 AND TAB_B.COL2=123 WHERE TAB_A.COL1=123;
Utilisation de définitions de tables imbriquées pour remplacer les vues Espace de travail
SELECT
CATENTRY.CATENTRY_ID, ATTRIBUTE.ATTRIBUTE_ID, ATTRIBUTE.NAME, ATTRIBUTE.LANGUAGE_ID, ATTRIBUTE.CATENTRY_ID,
ATTRVALUE.ATTRVALUE_ID, ATTRVALUE.ATTRIBUTE_ID, ATTRVALUE.CATENTRY_ID, ATTRVALUE.STRINGVALUE
FROM CATENTRY, ATTRVALUE, ATTRIBUTE
WHERE
ATTRVALUE.CATENTRY_ID = CATENTRY.CATENTRY_ID
AND CATENTRY.CATENTRY_ID IN (10683)
AND ATTRVALUE.LANGUAGE_ID IN ( -1,-2,-3,-4,-5 )
AND ATTRIBUTE.LANGUAGE_ID=ATTRVALUE.LANGUAGE_ID
AND ATTRIBUTE.ATTRIBUTE_ID=ATTRVALUE.ATTRIBUTE_ID
ORDER BY
ATTRIBUTE.SEQUENCE
SELECT
CATENTRY.CATENTRY_ID, ATTRIBUTE.ATTRIBUTE_ID, ATTRIBUTE.NAME, ATTRIBUTE.LANGUAGE_ID, ATTRIBUTE.CATENTRY_ID,
ATTRVALUE.ATTRVALUE_ID, ATTRVALUE.ATTRIBUTE_ID, ATTRVALUE.CATENTRY_ID, ATTRVALUE.STRINGVALUE
FROM (
SELECT
CATENTRY_ID
FROM
DB2INST1.CATENTRY
WHERE NOT EXISTS (
SELECT '1'
FROM WCW101.CATENTRY
WHERE DB2INST1.CATENTRY.CATENTRY_ID = WCW101.CATENTRY.CATENTRY_ID)
UNION ALL
SELECT
CATENTRY_ID
FROM
WCW101.CATENTRY
WHERE WCW101.CATENTRY.CONTENT_STATUS <> 'D'
) CATENTRY,
(
SELECT
ATTRVALUE_ID,LANGUAGE_ID,CATENTRY_ID,ATTRIBUTE_ID,STRINGVALUE
FROM
DB2INST1.ATTRVALUE
WHERE NOT EXISTS (
SELECT '1'
FROM WCW101.ATTRVALUE
WHERE DB2INST1.ATTRVALUE.ATTRVALUE_ID = WCW101.ATTRVALUE.ATTRVALUE_ID)
UNION ALL
SELECT
ATTRVALUE_ID,LANGUAGE_ID,CATENTRY_ID,ATTRIBUTE_ID,STRINGVALUE
FROM
WCW101.ATTRVALUE
WHERE WCW101.ATTRVALUE.CONTENT_STATUS <> 'D'
) ATTRVALUE,
(
SELECT
ATTRIBUTE_ID,LANGUAGE_ID,CATENTRY_ID,SEQUENCE,NAME
FROM
DB2INST1.ATTRIBUTE
WHERE NOT EXISTS (
SELECT '1'
FROM WCW101.ATTRIBUTE
WHERE DB2INST1.ATTRIBUTE.ATTRIBUTE_ID = WCW101.ATTRIBUTE.ATTRIBUTE_ID)
UNION ALL
SELECT
ATTRIBUTE_ID,LANGUAGE_ID,CATENTRY_ID,SEQUENCE,NAME
FROM
WCW101.ATTRIBUTE
WHERE WCW101.ATTRIBUTE.CONTENT_STATUS <> 'D'
) ATTRIBUTE
WHERE
ATTRVALUE.CATENTRY_ID = CATENTRY.CATENTRY_ID
AND CATENTRY.CATENTRY_ID IN (10683)
AND ATTRVALUE.LANGUAGE_ID IN ( -1,-2,-3,-4,-5 )
AND ATTRIBUTE.LANGUAGE_ID=ATTRVALUE.LANGUAGE_ID
AND ATTRIBUTE.ATTRIBUTE_ID=ATTRVALUE.ATTRIBUTE_ID
ORDER BY
ATTRIBUTE.SEQUENCE
LANGUAGE_ID IN ( -1,-2,-3,-4,-5 ) est inséré dans la définition de la table imbriquée ATTRVALUE dans la requête suivante :
SELECT
CATENTRY.CATENTRY_ID, ATTRIBUTE.ATTRIBUTE_ID, ATTRIBUTE.NAME, ATTRIBUTE.LANGUAGE_ID, ATTRIBUTE.CATENTRY_ID,
ATTRVALUE.ATTRVALUE_ID, ATTRVALUE.ATTRIBUTE_ID, ATTRVALUE.CATENTRY_ID, ATTRVALUE.STRINGVALUE
FROM (
SELECT
CATENTRY_ID
FROM
DB2INST1.CATENTRY
WHERE NOT EXISTS (
SELECT '1'
FROM WCW101.CATENTRY
WHERE DB2INST1.CATENTRY.CATENTRY_ID = WCW101.CATENTRY.CATENTRY_ID)
UNION ALL
SELECT
CATENTRY_ID
FROM
WCW101.CATENTRY
WHERE WCW101.CATENTRY.CONTENT_STATUS <> 'D'
) CATENTRY,
(
SELECT
ATTRVALUE_ID,LANGUAGE_ID,CATENTRY_ID,ATTRIBUTE_ID,STRINGVALUE
FROM
DB2INST1.ATTRVALUE
WHERE NOT EXISTS (
SELECT '1'
FROM WCW101.ATTRVALUE
WHERE DB2INST1.ATTRVALUE.ATTRVALUE_ID = WCW101.ATTRVALUE.ATTRVALUE_ID)
AND DB2INST1.ATTRVALUE.LANGUAGE_ID IN ( -1,-2,-3,-4,-5 )
UNION ALL
SELECT
ATTRVALUE_ID,LANGUAGE_ID,CATENTRY_ID,ATTRIBUTE_ID,STRINGVALUE
FROM
WCW101.ATTRVALUE
WHERE WCW101.ATTRVALUE.CONTENT_STATUS <> 'D'
AND WCW101.ATTRVALUE.LANGUAGE_ID IN ( -1,-2,-3,-4,-5 )
) ATTRVALUE,
(
SELECT
ATTRIBUTE_ID,LANGUAGE_ID,CATENTRY_ID,SEQUENCE,NAME
FROM
DB2INST1.ATTRIBUTE
WHERE NOT EXISTS (
SELECT '1'
FROM WCW101.ATTRIBUTE
WHERE DB2INST1.ATTRIBUTE.ATTRIBUTE_ID = WCW101.ATTRIBUTE.ATTRIBUTE_ID)
UNION ALL
SELECT
ATTRIBUTE_ID,LANGUAGE_ID,CATENTRY_ID,SEQUENCE,NAME
FROM
WCW101.ATTRIBUTE
WHERE WCW101.ATTRIBUTE.CONTENT_STATUS <> 'D'
) ATTRIBUTE
WHERE
ATTRVALUE.CATENTRY_ID = CATENTRY.CATENTRY_ID
AND CATENTRY.CATENTRY_ID IN (10683)
AND ATTRIBUTE.LANGUAGE_ID=ATTRVALUE.LANGUAGE_ID
AND ATTRIBUTE.ATTRIBUTE_ID=ATTRVALUE.ATTRIBUTE_ID
ORDER BY
ATTRIBUTE.SEQUENCE
$CM:READ$, $CM:WRITE$ et $CM:BASE$. Elles sont remplacées en phase d'exécution par les noms de schéma READ, WRITE et BASE lorsque le code SQL est exécuté par la couche service de données.Voir Modèle d'espace de données pour plus d'informations.
BEGIN_XPATH_TO_SQL_STATEMENT
name=/CatalogEntry[CatalogEntryIdentifier[(UniqueID=)]]+IBM_Attributes
base_table=CATENTRY
sql=
SELECT CATENTRY.$COLS:CATENTRY_ID$, ATTRIBUTE.$COLS:ATTRIBUTE$,ATTRVALUE.$COLS:ATTRVALUE$
FROM CATENTRY, ATTRVALUE, ATTRIBUTE
WHERE
ATTRVALUE.CATENTRY_ID = CATENTRY.CATENTRY_ID
AND CATENTRY.CATENTRY_ID IN (?UniqueID?)
AND ATTRVALUE.LANGUAGE_ID IN ($CONTROL:LANGUAGES$)
AND ATTRIBUTE.LANGUAGE_ID=ATTRVALUE.LANGUAGE_ID
AND ATTRIBUTE.ATTRIBUTE_ID=ATTRVALUE.ATTRIBUTE_ID
ORDER BY
ATTRIBUTE.SEQUENCE
<!-- the following query is optimized for workspaces -->
cm
sql=
SELECT CATENTRY.$COLS:CATENTRY_ID$, ATTRIBUTE.$COLS:ATTRIBUTE$,ATTRVALUE.$COLS:ATTRVALUE$
FROM (
SELECT
CATENTRY_ID
FROM
$CM:BASE$.CATENTRY
WHERE NOT EXISTS (
SELECT '1'
FROM $CM:WRITE$.CATENTRY
WHERE $CM:BASE$.CATENTRY.CATENTRY_ID = $CM:WRITE$.CATENTRY.CATENTRY_ID)
UNION ALL
SELECT
CATENTRY_ID
FROM
$CM:WRITE$.CATENTRY
WHERE $CM:WRITE$.CATENTRY.CONTENT_STATUS <> 'D'
) CATENTRY,
(
SELECT
ATTRVALUE_ID,LANGUAGE_ID,CATENTRY_ID,ATTRIBUTE_ID,STRINGVALUE
FROM
$CM:BASE$.ATTRVALUE
WHERE NOT EXISTS (
SELECT '1'
FROM $CM:WRITE$.ATTRVALUE
WHERE $CM:BASE$.ATTRVALUE.ATTRVALUE_ID = $CM:WRITE$.ATTRVALUE.ATTRVALUE_ID)
AND $CM:BASE$.ATTRVALUE.LANGUAGE_ID IN ($CONTROL:LANGUAGES$)
UNION ALL
SELECT
ATTRVALUE_ID,LANGUAGE_ID,CATENTRY_ID,ATTRIBUTE_ID,STRINGVALUE
FROM
$CM:WRITE$.ATTRVALUE
WHERE $CM:WRITE$.ATTRVALUE.CONTENT_STATUS <> 'D'
AND $CM:WRITE$.ATTRVALUE.LANGUAGE_ID IN ($CONTROL:LANGUAGES$)
) ATTRVALUE,
(
SELECT
ATTRIBUTE_ID,LANGUAGE_ID,CATENTRY_ID,SEQUENCE,NAME
FROM
$CM:BASE$.ATTRIBUTE
WHERE NOT EXISTS (
SELECT '1'
FROM $CM:WRITE$.ATTRIBUTE
WHERE $CM:BASE$.ATTRIBUTE.ATTRIBUTE_ID = $CM:WRITE$.ATTRIBUTE.ATTRIBUTE_ID)
UNION ALL
SELECT
ATTRIBUTE_ID,LANGUAGE_ID,CATENTRY_ID,SEQUENCE,NAME
FROM
$CM:WRITE$.ATTRIBUTE
WHERE $CM:WRITE$.ATTRIBUTE.CONTENT_STATUS <> 'D'
) ATTRIBUTE
WHERE
ATTRVALUE.CATENTRY_ID = CATENTRY.CATENTRY_ID
AND CATENTRY.CATENTRY_ID IN (?UniqueID?)
AND ATTRIBUTE.LANGUAGE_ID=ATTRVALUE.LANGUAGE_ID
AND ATTRIBUTE.ATTRIBUTE_ID=ATTRVALUE.ATTRIBUTE_ID
ORDER BY
ATTRIBUTE.SEQUENCE
END_XPATH_TO_SQL_STATEMENT
Fractionnement des requêtes
Si l'application de ces techniques ne donne pas les améliorations de performance nécessaires, vous pouvez choisir de fractionner les requêtes qui joignent de nombreuses tables en plusieurs requêtes. L'objectif de ce fractionnement est de réduire le nombre de vues utilisées dans la jointure. Si vous fractionnez une requête à étape unique, vous devrez la convertir en requête à deux étapes, avec une ou plusieurs requêtes d'association. Lors du fractionnement de l'une des instructions SQL d'association pour une requête en deux étapes, vous devez ajouter des instructions SQL d'association supplémentaires à la définition de profil d'accès. Lorsque des requêtes sont fractionnées, vous devez utiliser des jointures internes à la place des jointures externes chaque fois que cela est possible.
Soyez prudent lorsque vous fractionnez des requêtes. Si vous utilisez trop souvent cette approche, vous risquez d'obtenir un grand nombre de requête à chaque sélection depuis une seule table. Dans ce cas, vous devrez effectuer des jointures de tables dans la mémoire en écrivant du code Java, par exemple à l'aide d'un composeur de diagramme. Dans certains cas, vous devrez fournir les résultats d'une requête dans une autre. Ces types de requête doivent être utilisés en dernier ressort, car des problèmes de maintenance, de personnalisation et de migration peuvent surgir.

Utilisation de la syntaxe d'expression commune dans Oracle
L'Oracle optimizer génère habituellement des requêtes très efficaces, où une table temporaire est créée en fonction de l'ensemble de résultats des vues de jointure de requêtes d'origine. Une table temporaire peut être créée à l'aide de la syntaxe d'expression de table commune.
Par exemple, la requête suivante qui extrait les valeurs d'attribut du produit peut s'exécuter de façon bien plus rapide après sa réécriture à l'aide de la syntaxe d'expression de table commune :
BEGIN_ASSOCIATION_SQL_STATEMENT
name=IBM_CatalogEntryAttributeValue
base_table=CATENTRY
sql =
SELECT
CATENTRY.$COLS:CATENTRY$, ATTRVALUE.$COLS:ATTRVALUE$,
ATTRVALUE2.$COLS:ATTRVALUE$
FROM CATENTRY, ATTRVALUE
JOIN ATTRIBUTE
ON ATTRIBUTE.LANGUAGE_ID = ATTRVALUE.LANGUAGE_ID
AND ATTRIBUTE.ATTRIBUTE_ID = ATTRVALUE.ATTRIBUTE_ID
LEFT OUTER JOIN ATTRVALUE ATTRVALUE2
ON ATTRIBUTE.ATTRIBUTE_ID = ATTRVALUE2.ATTRIBUTE_ID
AND ATTRVALUE2.CATENTRY_ID = 0
AND ATTRIBUTE.LANGUAGE_ID = ATTRVALUE2.LANGUAGE_ID
WHERE CATENTRY.CATENTRY_ID IN ( $ENTITY_PKS$)
AND ATTRVALUE.CATENTRY_ID = CATENTRY.CATENTRY_ID
AND ATTRVALUE.LANGUAGE_ID IN ($CTX:LANG_ID$)
END_ASSOCIATION_SQL_STATEMENT
Utilisation de la syntaxe d'expression commune :
BEGIN_ASSOCIATION_SQL_STATEMENT
name=IBM_CatalogEntryAttributeValue
base_table=CATENTRY
sql =
WITH TEMP_TABLE AS (
SELECT
CATENTRY.CE_$COLS:CATENTRY$, ATTRVALUE.ATTR_$COLS:ATTRVALUE$,
ATTRVALUE2.ATTR2_$COLS:ATTRVALUE$
FROM CATENTRY, ATTRVALUE
JOIN ATTRIBUTE
ON ATTRIBUTE.LANGUAGE_ID = ATTRVALUE.LANGUAGE_ID
AND ATTRIBUTE.ATTRIBUTE_ID = ATTRVALUE.ATTRIBUTE_ID
LEFT OUTER JOIN ATTRVALUE ATTRVALUE2
ON ATTRIBUTE.ATTRIBUTE_ID = ATTRVALUE2.ATTRIBUTE_ID
AND ATTRVALUE2.CATENTRY_ID = 0
AND ATTRIBUTE.LANGUAGE_ID = ATTRVALUE2.LANGUAGE_ID
WHERE CATENTRY.CATENTRY_ID IN ( $ENTITY_PKS$)
AND ATTRVALUE.CATENTRY_ID = CATENTRY.CATENTRY_ID
AND ATTRVALUE.LANGUAGE_ID IN ($CTX:LANG_ID$)
) SELECT * FROM TEMP_TABLE
END_ASSOCIATION_SQL_STATEMENT
Cette technique permet à l'optimiseur d'Oracle d'insérer les prédicats nécessaires dans les vues. Comme davantage de données sont éliminées dans les premiers stades du traitement de la requête, les jointures suivantes seront appliquées à un ensemble de données bien plus petit. Cette solution permet souvent d'obtenir des améliorations notables au niveau des performances.