Tous les produits
Search
Centre de documentation

PolarDB:Optimisation des sous-requêtes

Dernière mise à jour :Aug 11, 2026

PolarDB for MySQL réécrit automatiquement les instructions SQL lors de la phase d'optimisation afin de supprimer les sous-requêtes redondantes et de pré-calculer celles qui sont constantes. Cette approche réduit la complexité du plan d'exécution et améliore les performances des requêtes, un problème fréquent lorsque les frameworks ORM (Object-Relational Mapping) génèrent des requêtes profondément imbriquées.

Périmètre

  • Série de produits : Cluster Edition et Standard Edition

  • Version du noyau : MySQL 8.0.2, révision 8.0.2.2.19 ou ultérieure

Activer l'optimisation des sous-requêtes

Contrôlez l'optimisation des sous-requêtes à l'aide du paramètre loose_simplify_subq_mode.

Le nom du paramètre varie selon l'endroit où vous le définissez :

  • Console PolarDB : Utilisez le préfixe loose_ (loose_simplify_subq_mode). Certains paramètres conservent ce préfixe pour assurer la compatibilité avec les fichiers de configuration MySQL.

  • Session de base de données (ligne de commande ou client) : Supprimez le préfixe loose_ et utilisez SET simplify_subq_mode = '...'.

Paramètre Niveau Description
loose_simplify_subq_mode Global/Session Contrôle l'optimisation des sous-requêtes. Valeurs valides : REPLICA_ON (par défaut) — active l'optimisation uniquement sur les nœuds en lecture seule (RO) ; ON — active la fonctionnalité ; OFF — désactive l'optimisation.

Fonctionnement

L'optimiseur applique trois types de réécritures durant la phase d'optimisation :

  1. Élimination des SELECT redondants — supprime les couches d'enveloppe SELECT inutiles autour des fonctions d'agrégation ou des expressions constantes.

  2. Pré-évaluation de [NOT] EXISTS — remplace les sous-requêtes EXISTS par TRUE ou FALSE lorsque le résultat peut être déterminé sans exécuter la sous-requête.

  3. ANY/ALL LIMIT injection — ajoute LIMIT 1 aux sous-requêtes à projection constante dans les clauses ANY ou ALL afin d'éviter les balayages complets de table.

Chaque scénario ci-dessous illustre la réécriture exacte appliquée par l'optimiseur.

Scénarios d'optimisation

Scénario 1 : Éliminer l'imbrication SELECT redondante

Cas d'application : La sous-requête enveloppe uniquement une fonction d'agrégation ou une expression, sans autre logique complexe.

Modèle de réécriture :

-- Before
SELECT (SELECT SUM(a) FROM t2) FROM dual;

-- After
SELECT SUM(`test`.`t2`.`a`) AS `sum(a)` FROM `test`.`t2`

La même élimination s'applique dans les clauses HAVING, où l'optimiseur aplatit plusieurs niveaux d'imbrication :

-- Before
SELECT SUM(a) FROM t2 HAVING (SELECT(SELECT(SELECT count(b))));

-- After
SELECT SUM(`testdb`.`t2`.`a`) AS `SUM(a)` from `testdb`.`t2` HAVING (0 <> count(`testdb`.`t2`.`b`))

Vérification par l'exemple :

  1. Préparez les données de test :

    DROP TABLE IF EXISTS t2;
    CREATE TABLE t2 (
        id INT PRIMARY KEY AUTO_INCREMENT,
        a INT,
        b INT
    );
    INSERT INTO t2 (a, b) VALUES (10, 100), (20, NULL), (50, 200), (120, NULL);
  2. Examinez le plan d'exécution sans optimisation :

    SET simplify_subq_mode = 'OFF';
    EXPLAIN SELECT SUM(a) FROM t2 HAVING (SELECT(SELECT(SELECT count(b))));
    SHOW WARNINGS;

    Résultat — quatre niveaux d'imbrication SELECT subsistent :

    /* select#1 */ select sum(`testdb`.`t2`.`a`) AS `SUM(a)` from `testdb`.`t2` having (0 <> (/* select#2 */ select (/* select#3 */ select (/* select#4 */ select count(`testdb`.`t2`.`b`)))))
  3. Examinez le plan d'exécution avec l'optimisation activée :

    SET simplify_subq_mode = 'ON';
    EXPLAIN SELECT SUM(a) FROM t2 HAVING (SELECT(SELECT(SELECT count(b))));
    SHOW WARNINGS;

    Résultat — toute imbrication est éliminée :

    /* select#1 */ select sum(`testdb`.`t2`.`a`) AS `SUM(a)` from `testdb`.`t2` having (0 <> count(`testdb`.`t2`.`b`))

Scénario 2 : Pré-évaluer les sous-requêtes [NOT] EXISTS

Cas d'application : L'optimiseur peut déterminer statiquement qu'une sous-requête EXISTS ou NOT EXISTS retourne toujours vrai (non vide) ou faux (vide).

Modèle de réécriture :

-- Non-empty result: EXISTS clause is removed entirely
-- Before
SELECT * FROM t1 WHERE EXISTS(SELECT MAX(a) FROM t2);
-- After
SELECT * FROM t1

-- Empty result (WHERE/HAVING evaluates to false, or LIMIT 0): replaced with false
-- Before
SELECT * FROM t1 WHERE EXISTS(SELECT max(a) FROM t2 HAVING 1=2);
-- After
SELECT * FROM t1 WHERE false

Cela empêche totalement l'exécution de la sous-requête.

Vérification par l'exemple :

  1. Préparez les données de test :

    DROP TABLE IF EXISTS t1;
    DROP TABLE IF EXISTS t2;
    CREATE TABLE t1 (id INT);
    CREATE TABLE t2 (val INT);
    INSERT INTO t1 VALUES (1), (2);
  2. Examinez le plan d'exécution sans optimisation :

    SET simplify_subq_mode = 'OFF';
    EXPLAIN SELECT * FROM t1 WHERE EXISTS(SELECT MAX(a) FROM t2);
    SHOW WARNINGS;

    Résultat — la sous-requête EXISTS est évaluée au moment de l'exécution :

    /* select#1 */ select `testdb`.`t1`.`id` AS `id` from `testdb`.`t1` where exists(/* select#2 */ select max(`testdb`.`t1`.`id`) from `testdb`.`t2`)
  3. Examinez le plan d'exécution avec l'optimisation activée :

    SET simplify_subq_mode = 'ON';
    EXPLAIN SELECT * FROM t1 WHERE EXISTS(SELECT MAX(a) FROM t2);
    SHOW WARNINGS;

    Résultat — la clause EXISTS a disparu :

    /* select#1 */ select `testdb`.`t1`.`id` AS `id` from `testdb`.`t1`

Scénario 3 : Ajouter LIMIT 1 aux projections constantes dans les sous-requêtes ANY/ALL

Cas d'application : La sous-requête située dans une clause ANY ou ALL sélectionne uniquement une constante (aucune référence de colonne). Récupérer plusieurs lignes identiques étant inefficace, l'optimiseur injecte LIMIT 1.

Modèle de réécriture :

-- Before
SELECT * FROM t1 WHERE a > ANY (SELECT 1 FROM t2);

-- After
SELECT * FROM t1 WHERE a > ANY (SELECT 1 FROM t2 LIMIT 1);

Cette technique évite un balayage complet de la table t2 lorsque le résultat constant est identique pour chaque ligne.

Mise en œuvre en production

Avant d'activer l'optimisation des sous-requêtes pour des charges de travail critiques, suivez ces étapes :

  • Exécutez d'abord des tests de régression complets dans un environnement de pré-production. Dans de rares cas limites — par exemple des requêtes dépendant d'un ordre d'exécution spécifique ou du nombre d'exécutions de sous-requêtes — l'optimisation peut entraîner un comportement différent, bien que l'équivalence logique soit garantie dans la plupart des situations.

  • Maintenez les statistiques des tables à jour. Le scénario 2 repose sur ces statistiques pour déduire si un ensemble est vide ou non. Exécutez régulièrement ANALYZE TABLE afin que l'optimiseur prenne des décisions précises.