# WP_Query en profondeur : une Query Loop qui joint deux méta-champs sans ralentir

> Une Query Loop filtrée sur deux champs personnalisés multiplie les jointures. Restructurer la requête, plutôt que la base, suffit souvent à retrouver un temps de réponse correct.

- Auteur : WordPress Développement
- Publié le : 2024-08-12
- Mis à jour le : 2024-08-12
- Catégorie : Éditeur de site (FSE)
- URL : https://www.wpmoderne.fr/fse/wp-query-profondeur-query-loop-deux-meta-champs/

## L’essentiel

- Chaque clause meta_query ajoute une jointure distincte sur wp_postmeta
- Fusionner deux champs liés en un seul au moment de l'enregistrement évite une jointure
- relation AND coûte plus cher que relation OR sur de gros volumes

Une Query Loop filtrée sur un seul champ personnalisé et une Query Loop filtrée sur deux champs combinés n'ont, en apparence, presque rien de différent dans leur configuration ; en pratique, la seconde peut multiplier le temps de réponse par un facteur bien supérieur au simple doublement qu'on pourrait attendre. La différence tient à la façon dont `WP_Query` traduit chaque clause `meta_query` en jointure SQL distincte sur la table `wp_postmeta`.

## L'arborescence d'une requête à deux méta-champs

Une Query Loop qui filtre des fiches produits sur un statut de stock et une plage de prix construit, en interne, une requête dont la structure logique ressemble à ceci :

```
WP_Query
├── meta_query (relation: AND)
│   ├── clause 1 : stock_statut = "disponible"
│   │   └── jointure vers wp_postmeta AS mt1
│   └── clause 2 : prix BETWEEN 20 AND 80
│       └── jointure vers wp_postmeta AS mt2
└── requête finale
    └── SELECT ... FROM wp_posts
        INNER JOIN wp_postmeta AS mt1 ON ...
        INNER JOIN wp_postmeta AS mt2 ON ...
        WHERE mt1.meta_value = 'disponible'
          AND mt2.meta_value BETWEEN 20 AND 80
```

Chaque clause ajoute sa propre jointure vers la même table, avec un alias distinct : la table `wp_postmeta` stocke chaque paire clé-valeur sur une ligne séparée, ce qui oblige à la joindre une fois par champ recherché plutôt qu'une seule fois pour l'ensemble.

## Ce que la relation AND coûte de plus qu'une relation OR

> L'essentiel à retenir : Chaque clause meta_query ajoute une jointure distincte sur wp_postmeta ; Fusionner deux champs liés en un seul au moment de l'enregistrement évite une jointure ; relation AND coûte plus cher que relation OR sur de gros volumes

Une relation `OR` entre deux clauses laisse à MySQL la liberté de satisfaire la condition via l'une ou l'autre jointure, ce qui limite souvent le nombre de lignes intermédiaires à combiner. Une relation `AND`, en revanche, exige que les deux jointures produisent un résultat cohérent pour chaque article, ce qui multiplie le nombre de combinaisons à évaluer sur un catalogue de plusieurs milliers de fiches — la différence devient nette dès que le volume dépasse quelques centaines d'entrées filtrées simultanément.

## Une restructuration possible : fusionner au moment de l'enregistrement

Quand les deux champs filtrés varient toujours ensemble d'un point de vue métier — ici, un produit disponible dans une fourchette de prix précise correspond à une catégorie commerciale bien définie — il devient possible de précalculer un champ composite au moment de l'enregistrement de la fiche, plutôt que de recombiner les deux champs à chaque requête :

```
add_action( 'save_post_produit', function ( $post_id ) {
    $statut = get_post_meta( $post_id, 'stock_statut', true );
    $prix   = (int) get_post_meta( $post_id, 'prix', true );

    $tranche = $prix < 20 ? 'bas' : ( $prix <= 80 ? 'moyen' : 'haut' );

    update_post_meta( $post_id, 'segment_recherche', $statut . '_' . $tranche );
} );
```

La Query Loop ne filtre alors plus que sur un seul champ, `segment_recherche`, avec une seule jointure au lieu de deux :

```
new WP_Query( array(
    'post_type' => 'produit',
    'meta_key'  => 'segment_recherche',
    'meta_value' => 'disponible_moyen',
) );
```

## Ce que cette restructuration ne résout pas

Cette approche fonctionne bien pour des filtres discrets et prévisibles à l'avance, comme une tranche de prix découpée en catégories fixes. Elle devient inadaptée dès que le filtre doit accepter une plage continue arbitraire choisie par le visiteur, auquel cas la fusion en un seul champ perdrait la précision nécessaire au filtre réel. Dans ce cas, la relation `AND` reste incontournable, et la restructuration porte alors sur la réduction du nombre de résultats à filtrer en amont, par exemple via un filtre de taxonomie appliqué avant la clause `meta_query`.

### Une variante intermédiaire

Quand seule la première clause peut être fusionnée mais pas la seconde, combiner un filtre de taxonomie natif — lui-même indépendant de `wp_postmeta` — avec une seule clause `meta_query` restante permet déjà de retirer une jointure sur les deux :

```
new WP_Query( array(
    'post_type' => 'produit',
    'tax_query' => array(
        array( 'taxonomy' => 'disponibilite', 'field' => 'slug', 'terms' => 'en-stock' ),
    ),
    'meta_key'   => 'prix',
    'meta_value' => array( 20, 80 ),
    'meta_compare' => 'BETWEEN',
    'meta_type'    => 'NUMERIC',
) );
```

> Avant d'ajouter une deuxième clause meta_query à une Query Loop existante, nous vérifions systématiquement si l'un des deux champs peut devenir une taxonomie plutôt qu'un champ personnalisé : la structure de requête qui en résulte reste presque toujours plus légère.

## Ce qu'il faut retenir

Deux clauses `meta_query` combinées en `AND` ne coûtent pas simplement deux fois le prix d'une seule clause : elles multiplient les combinaisons évaluées par MySQL, en particulier sur un volume de contenu conséquent. Restructurer la donnée en amont, en fusionnant ce qui peut l'être ou en déplaçant un champ vers une taxonomie, reste souvent une solution plus durable que d'accepter la requête telle quelle et de chercher à en compenser le coût ailleurs.
