قد تبطؤ صفحةٌ وليس فيها استعلام بطيء واحد. عشرون منتجًا، ولكلٍّ منها استعلامٌ لتصنيفه وآخر لسعره وآخر لصورته: واحدٌ وستّون استعلامًا حيث يكفي اثنان. كلٌّ منها يأخذ جزأين من الألف، وتأخذ الصفحة ثانيةً ونصفًا.

عُدّها أوّلًا

// at the end of the page
echo count($pdo->query('SHOW SESSION STATUS LIKE "Questions"')->fetchAll());

أو اقرأ العدد من شريط التنقيح في إطار عملك، أو من سجلّ البطء بعد ضبط long_query_time على 0 لطلبٍ واحد. فالعدد أهمّ من أيّ توقيتٍ منفرد: دون العشرين معتاد، وفوق المائة هو العلّة.

كيف يبدو في الشفرة

// 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);
}

العلاج ضمٌّ، أو استعلامٌ واحد لكلّ المعرّفات

$ids = array_column($products, 'category_id');
$cats = query('SELECT * FROM categories WHERE id IN (' . placeholders($ids) . ')', $ids);
// then match them up in PHP

استعلامان بدل واحدٍ وعشرين، ولا يسوء الأمر حين تعرض الصفحة مائة منتج — وهذه هي الخاصّة التي تعني فعلًا.

في مُخطِّط علاقيّ

لهذا وُجد التحميل المتلهّف. فالافتراضُ الكسول مريح ويُنتج هذا الشكل بعينه، والعلاج كلمةٌ واحدة في الغالب.

$products = Product::with('category', 'images')->limit(20)->get();
وتخزينُ صفحةٍ من هذا النوع يخفيه حتى يبرد المخزن، فينال أوّلُ زائرٍ بعد كلّ نشرٍ الأربعمائة استعلامٍ كاملة. أصلح العدد ثمّ خزّن.
راقب عدد الاستعلامات وهي تنمو مع الصفحة. فصفحةٌ تُجري ثلاثة لصفٍّ واحد وثلاثمائة لمائة صفٍّ لها هذا الشكل، مهما قالت التوقيتات اليوم.