بنناجح
المرحلة 1 · أساسيات الداتابيز الدرس 3 من 3

الكويري بتتنفذ بأنهي ترتيب؟

الكويري بتتكتب بترتيب وبتتنفذ بترتيب تاني خالص. لو فهمت ترتيب التنفيذ، هتفهم لوحدك ليه أخطاء كتير بتحصل: الـ alias اللي مش موجود، و count اللي مش راضية تشتغل في WHERE، و NULL اللي بيختفي.

في الدرس ده هتتعلم
  • تفرق بين SQL كلغة "قول عايز إيه" وبين الكود اللي بيمشي صف صف
  • ترتّب أجزاء الكويري بترتيب تنفيذها المنطقي، وتستخدمه تتوقع الأخطاء قبل ما تحصل
  • تعرف ليه الـ alias شغال في ORDER BY ومش شغال في WHERE، وليه التجميع مكانه HAVING
  • تحط الـ 5 أسئلة اللي بتحل بيهم أي مسألة SQL

قول عايز إيه، مش تعمله إزاي

لو عايز أسماء المنتجات اللي سعرها فوق 50 ألف، في PHP هتكتب حاجة زي كده:

products.php
$result = [];
foreach ($products as $p) {          // صف صف
    if ($p['price'] > 50000) {
        $result[] = $p['name'];
    }
}

انت هنا بتقول للكمبيوتر يعمل إيه خطوة خطوة. في SQL بتوصف النتيجة اللي عايزها وخلاص:

نفس الحاجة بـ SQL
SELECT name FROM products WHERE price > 50000;

مين اللي قرر يمشي على الصفوف إزاي؟ يستخدم Index ولا يقرا الجدول كله؟ ده الـ Optimizer جوه الداتابيز. ودي فكرة اسمها Declarative.

الكويري بتتنفذ بترتيب غير اللي اتكتبت بيه

بتكتب SELECT الأول، بس الداتابيز مش بتبدأ بيها. ده الترتيب المنطقي:

بتتكتب بتتنفذ بتعمل إيه
SELECT 5 تختار الأعمدة وتحسبها
FROM 1 تجيب الجدول (أو الجداول وتربطها)
WHERE 2 تشيل الصفوف اللي مش مطابقة
GROUP BY 3 تجمّع الصفوف في مجموعات
HAVING 4 تشيل المجموعات اللي مش مطابقة
ORDER BY 6 ترتّب النتيجة
LIMIT 7 تاخد أول كام صف

الكلام ده سهل يتحفظ وسهل يتنسي. فبدل ما تحفظه، اتفرج عليه: المحاكي ده بيشغّل كل خطوة على PostgreSQL الحقيقي ويوريك الصفوف بعد كل خطوة. جرّب الأمثلة اللي فوق، أو عدّل أي جزء ودوس Enter.

الكويري بتتنفذ إزاي؟محاكاة
جرّب:

عدّل أي جزء ودوس Enter. سيبه فاضي لو مش عايزه.

بتتكتب كده، والرقم = ترتيب التنفيذ

جرّب “الـ NULL بيختفي”: الشرط price < 30000 OR price >= 30000 المفروض يغطي كل الأرقام، بس الـ WHERE رجّعت 17 صف من 18. المنتج اللي سعره NULL الشرط بتاعه طلع مش معروف (علامة ؟)، والـ WHERE بتعدّي اللي TRUE بس. وجرّب “أغلى 3”: أول صف طلع المنتج اللي ملوش سعر! في PostgreSQL، الـ NULL بيتحسب أكبر من أي قيمة في الترتيب، فـ DESC بيحطه في الأول.

رتّب

دوس على أجزاء الكويري بالترتيب اللي بتتنفذ بيه فعلاً:

ليه الترتيب ده بيفرق؟ 4 نتايج مباشرة

1. الـ alias مش موجود في WHERE

الـ AS with_vat بيتعمل في خطوة 5 (SELECT). والـ WHERE بتشتغل في خطوة 2، يعني قبلها. فـ with_vat لسه ما اتولدش:

هتفشل
SELECT name, price * 1.14 AS with_vat
FROM products
WHERE with_vat > 60000;

الحل: كرر الحسبة في الـ WHERE (WHERE price * 1.14 > 60000)، أو استخدم Subquery أو CTE (المرحلة 5).

2. بس الـ alias موجود في ORDER BY

الـ ORDER BY خطوة 6، بعد الـ SELECT. فالـ alias اتعمل خلاص:

هتشتغل
SELECT name, price * 1.14 AS with_vat
FROM products
ORDER BY with_vat DESC;

3. التجميع مينفعش في WHERE

count(*) بتعدّ صفوف المجموعة. والمجموعات بتتعمل في خطوة 3، والـ WHERE في خطوة 2. يعني وقت الـ WHERE لسه مفيش مجموعات تتعد:

هتفشل
SELECT brand_id, count(*)
FROM products
WHERE count(*) > 3
GROUP BY brand_id;

ودي بالظبط وظيفة HAVING: WHERE للمجموعات، وبتشتغل في خطوة 4 بعد ما المجموعات اتعملت. غيّر WHERE count(*) > 3 لـ HAVING count(*) > 3 وحطها بعد الـ GROUP BY.

4. LIMIT من غير ORDER BY = “أي كام صف”

الـ LIMIT آخر خطوة، بتقص النتيجة اللي جاتلها. لو مفيش ORDER BY، النتيجة ملهاش ترتيب (فاكر الدرس الأول؟)، فـ “أول 3” ملهاش معنى ثابت.

سؤال✓

الكويري دي هتشتغل؟ SELECT brand_id, count(*) AS n FROM products GROUP BY brand_id ORDER BY n DESC;

سؤال✓

عايز الطلبات اللي اتعملت في 2024 بس، وبعدين تعدّهم لكل حالة. الشرط على السنة مكانه فين؟

الـ Grain بعد كل خطوة

ارجع للمحاكي وجرّب “عدد المنتجات لكل ماركة”، وبص على خطوة GROUP BY: كان عندك 18 صف (كل صف = منتج)، وبقى عندك 6 (كل صف = ماركة). الـ Grain اتغيّر.

ومن اللحظة دي، أي عمود هتطلبه في الـ SELECT لازم يبقى ليه قيمة واحدة للماركة كلها: يا إما عمود من أعمدة الـ GROUP BY (brand_id)، يا إما تجميع (count(*)، max(price)). طب name؟ الماركة فيها كذا منتج، فأنهي اسم يطلع؟ علشان كده PostgreSQL بيرفض:

أنهي name؟
SELECT brand_id, name, count(*)
FROM products
GROUP BY brand_id;
سؤال✓

SELECT brand_id, count(*) FROM products GROUP BY brand_id — كل صف في النتيجة بيمثّل إيه؟

خطة حل أي مسألة

كل اللي فات بيتلخص في 5 أسئلة. اسألهم لنفسك قبل ما تكتب أي حرف، في أي تمرين هنا، أو في أي مسألة على LeetCode، أو في الإنترفيو:

  1. النتيجة صف لكل إيه؟ (Grain) عميل؟ طلب؟ ماركة في شهر؟
  2. محتاج أنهي جداول؟ ومربوطة إزاي؟ والربط ده هيزوّد الصفوف؟
  3. الفلتر قبل التجميع ولا بعده؟ شرط على الصف = WHERE. على المجموعة = HAVING.
  4. كل صف محتاج يشوف صفوف تانية؟ (ترتيب، اللي قبله، تراكمي) يبقى Window Function. دي في المرحلة 5.
  5. الحالات الطرفية؟ NULL، تعادل، تكرار، نتيجة فاضية.

اتمرّن

التمرين ده أول واحد عليه Test مستخبي: حلك هيتجرّب على الداتا اللي قدامك، وعلى نسخة تانية فيها منتج زيادة ملوش سعر.

تمرين تطبيقي

متوسط+1 اختبار مستخبي✓ اتحل

اعرض اسم وسعر كل المنتجات من الأغلى للأرخص، بس المنتجات اللي ملهاش سعر تيجي في الآخر مش في الأول. الترتيب هنا جزء من الإجابة.

💡 تلميح

فاكر "أغلى 3" في المحاكي؟ الـ NULL طلع الأول مع DESC. PostgreSQL عنده NULLS LAST بعد اتجاه الترتيب.

تمرين تطبيقي

سهل✓ اتحل

اعرض اسم كل منتج، وسعره بعد ضريبة 14% في عمود اسمه with_vat، ورتّب من الأرخص للأغلى بالسعر بعد الضريبة. استخدم الـ alias في الترتيب.

💡 تلميح

الحسبة في الـ SELECT باسم with_vat، والترتيب بالاسم ده. الترتيب التصاعدي هو الافتراضي، والـ NULL بييجي في الآخر لوحده هنا.