Una página puede ir lenta sin una sola consulta lenta. Veinte productos, y por cada uno una consulta para su categoría, su precio y su imagen: sesenta y una consultas donde bastarían dos. Cada una tarda dos milisegundos y la página tarda segundo y medio.
Cuéntelas primero
// at the end of the page
echo count($pdo->query('SHOW SESSION STATUS LIKE "Questions"')->fetchAll());
O lea la cuenta en la barra de depuración del framework, o en el registro de lentas con long_query_time a 0 durante una sola petición. El número importa más que cualquier medición aislada: por debajo de 20 es normal, por encima de 100 es el fallo.
Cómo se ve en el código
// N+1: one query, then one more per row
$products = query('SELECT * FROM products LIMIT 20');
foreach ($products as $p) {
$p->category = query('SELECT * FROM categories WHERE id = ?', $p->category_id);
}
El remedio es un join, o una consulta para todos los identificadores
$ids = array_column($products, 'category_id');
$cats = query('SELECT * FROM categories WHERE id IN (' . placeholders($ids) . ')', $ids);
// then match them up in PHP
Dos consultas en lugar de veintiuna, y no empeora cuando la página muestra cien productos, que es justamente la propiedad que importa.
En un ORM
Para esto existe la carga anticipada. El valor perezoso por defecto es cómodo y produce exactamente esta forma, y el remedio suele ser una sola palabra.
$products = Product::with('category', 'images')->limit(20)->get();