Les fetch modes de PDO 3 : les modes spéciaux

Suite de ma série consacrée aux modes de récupération de PDO, voici les modes “spéciaux”, c’est à dire des modes au comportement particulier, très mal documentés mais souvent très pratiques !

FETCH_LAZY

Il se comporte comme un mélange de FETCH_OBJ et FETCH_ASSOC puisque l’objet retourné peut être accédé comme un objet ($r->id) ou comme un tableau associatif ($r['id']). Mais, comme son nom l’indique, il semble faire des bidouilles interressantes au niveau de la gestion de la mémoire ; d’après la documentation de PHP PDO::FETCH_LAZY crée les noms des variables de l’objet comme ils sont rencontrés.

A noter que ce mode ne peut-être utilisé qu’avec fetch, sans doute en raison de la gestion de la mémoire. De plus, le resultat affiché avec print_r ne semble par correspondre puisqu’on obtient :

PDORow Object
(
  [queryString] => SELECT id, login FROM user LIMIT 0,2
  [id] => 1
  [login] => toto
)
PDORow Object
(
  [queryString] => SELECT id, login FROM user LIMIT 0,2
  [id] => 2
  [login] => titi
)

Les performances par rapport à FETCH_BOTH sont étonnament bonnes (en comparant avec while + fetch) :

  • Temps de récupération : -18,9%
  • Taille du résultat : -77,8%

Toutefois, ces résultats doivent être considérés avec précaution : puisque ce mode n’utilise pas la mémoire de la même façon que les autres, mon script de test ne donne pas forcement des résultats pertinants. En pratique, sur une application réelle, pour afficher une liste d’éléments par exemple, en utilisant FETCH_LAZY plutôt que FETCH_ASSOC, le gain de mémoire est minime (de l’ordre de 0,05Ko, certes c’est toujours bon à prendre) et je n’observe pas de gain de vitesse significatif.

Pour l’instant, j’utilise ce mode quand les résultats ne vont pas être utilisés immédiatement, par exemple dans le cadre d’une architecture MVC où la requête est effetuée dans le modèle (ou dans le controlleur) et les résultats ne sont récupérés et affichés que plus tard dans la vue.

FETCH_COLUMN

Ce mode permet de récupérer uniquement une colonne, en précisant son numéro. Il fonctionne avec setFechMode, fetchAll et même directement query. Il fonctionne également avec fetch, mais il n’est pas possible de préciser le numéro de la colonne à récupérer (défaut 0). Les colonnes sont numérotées à partir de 0.

Attention, lorsque vous utilisez setFetchMode pour préciser le mode, il n’y a pas de valeur par défaut pour le numéro de colonne. Donc si le deuxième paramètre est absent, PDO retourne au mode par défaut FETCH_BOTH.

Exemple :

$results->fetchAll(PDO::FETCH_COLUMN, 1) // colonne 1 = le login
Array
(
  [0] => toto
  [1] => titi
)

Par rapport à FETCH_BOTH :

  • Temps de récupération : -18,8%
  • Taille du résultat : Non significatif (une seule colonne retournée)

Selon moi, pour une requête avec plusieurs colonnes, ce mode est inutile et présente les même iconvénients que FETCH_NUM (le numéro des colonnes changent si on change la requête, ce qui peut casser toute l’application). Autant modifier la requête pour ne récupérer qu’une seule colonne, en plus on économise sur le temps de transfert entre la base de données et l’application.

Et justement, ce mode devient réellement interressant lorsqu’il est utilisé pour une requête d’une seule colonne. En effet, avec FETCH_NUM on obtient un tableau de tableaux d’une seule case, par exemple :

Array
(
  [0] => Array
    (
      [0] => toto
    )
  [1] => Array
    (
      [0] => titi
    )
)

Avec FETCH_COLUMN, le resultat est donc nettement plus petit (et directement exploitable, parcequ’un tableau de tableaux c’est vraiment pas pratique), et le temps de récupération est légerement plus court :

  • Temps de récupération : -9%
  • Taille du résultat : -62% (!)

En résumé, pour récupérer une liste de valeurs, utilisez FETCH_COLUMN !

FETCH_KEY_PAIR

Nous venons de voir une méthode simple d’optimisation pour les requetes à une colonne. FETCH_KEY_PAIR est en quelque sorte l’équivalent pour les requêtes à 2 colonnes : il permet d’obtenir directement un tableau associatif avec la première colonne comme clef et la seconde comme valeur. J’adore !

Attention : ce mode ne fonctionne qu’avec fetchAll (lorsqu’on l’utilise avec setFetchMode ça provoque une erreur, voir bug #42917).

Exemple :

// SELECT id, login FROM user u';
$results->fetchAll(PDO::FETCH_KEY_PAIR);
// id => login
Array
(
  [1] => toto
  [2] => titi
)

Par rapport avec FETCH_BOTH :

  • Temps de récupération : -14,8%
  • Taille du résultat : -78% (!)

Ce mode est tout simplement génial dès qu’il s’agit de récupérer des couples de valeurs. Un exemple de cas très fréquent pour moi est de récupérer un id et un label dans une table de référence pour construire une liste déroulante dans un formulaire. Utiliser FETCH_KEY_PAIR au lieu de FETCH_NUM (ou FETCH_ASSOC) puis de construire le tableau associatif à la main permet d’économiser un peu de temps et beaucoup de mémoire. Bref, FETCH_KEY_PAIR c’est bon, mangez-en.

FETCH_BOUND

Ce mode permet d’extraire les valeurs directement dans des variables PHP, préalablement liées aux colonnes. Personnellement je ne comprends pas l’interet de ce mode et je ne l’ai jamais utilisé, mais si ça interresse quelqu’un, on peut trouver des exemples dans la doc de la fonction bindColumn.

FETCH_FUNC

Ce mode permet de définir une fonction callback pour récupérer les données. Ce mode ne fonctionne qu’avec fetchAll (si vous essayez de l’utiliser avec setFetchMode vous aurez droit à un joli message d’erreur du genre SQLSTATE[HY000]: General error: PDO::FETCH_FUNC is only allowed in PDOStatement::fetchAll()).

La fonction callback reçoit en paramètre toutes les colonnes (func_get_args peut être très utile) et doit retourner le résultat voulu.

Exemple pour émuler le comportement de FETCH_ASSOC :

function my_callback($id, $login)
{
    return array('id' => $id, 'login' => $login);
}
$results->fetchAll(PDO::FETCH_FUNC, 'my_callback');
Array
(
  [0] => Array
    (
      [id] => 1
      [login] => toto
    )

  [1] => Array
    (
      [id] => 2
      [login] => titi
    )
)

Le temps d’execution dépend évidemment du contenu de la fonction. A titre indicatif, avec le callback présenté en exemple, qui émule FETCH_ASSOC prend 70% plus de temps que la version native.

A suivre : les modes modificateurs.

One thought on “Les fetch modes de PDO 3 : les modes spéciaux

  1. Pingback: Tilàcica » Blog Archive » 10 bonnes raisons d’utiliser PDO (PHP Data Object)

Comments are closed.