تعبير CASE في SQLite: بديل if/else داخل الاستعلام
CASE هو الطريقة التي تكتب بها منطقًا شرطيًا داخل الاستعلام. يمر على فروع WHEN واحدًا تلو الآخر، يختار أول فرع تتحقق شرطه، ويُرجع القيمة المكتوبة بعد THEN. وإذا لم يتطابق أي فرع، تُرجع القيمة الموجودة في ELSE — أو NULL إن لم تكتب ELSE أصلًا.
الكلمة المفتاحية هنا هي تعبير (expression). فـ CASE يُنتج قيمة، ولذلك يمكن استخدامه في أي موضع تُقبل فيه القيم: عمود في SELECT، أو مفتاح ترتيب داخل ORDER BY، أو الطرف الآخر في عملية مقارنة، أو حتى وسيطًا تمرّره لدالة.
هذا هو الشكل العام: CASE، يتبعها WHEN ... THEN ... مرة أو أكثر، ثم ELSE اختيارية، وأخيراً END. كلمة END إلزامية — ونسيانها هو الخطأ الإملائي الأكثر شيوعاً.
مثال واقعي على CASE WHEN في SQLite
لنفترض أنك تملك جدول طلبات وتريد تصنيف كل صف حسب حجمه. لنُنشئ جدولاً صغيراً مباشرةً حتى تتمكن من تجربة الاستعلام:
تُفحص الفروع من الأعلى إلى الأسفل، وأول تطابق هو الذي يفوز، لذلك رتّبها من الأكثر تحديدًا إلى الأكثر عمومية. أما ELSE فهو يلتقط كل ما لم يتطابق مع الشروط السابقة — وبدونه ستحصل على NULL بدلًا من 'كبير' للقيمة 1200.00.
الفرق بين Searched وSimple في تعبير CASE
ما رأيته بالأعلى هو الصيغة المبحوث عنها (searched)، حيث يحمل كل WHEN شرطه المنطقي الخاص. لكن هناك صيغة أقصر تُستخدم عند مقارنة تعبير واحد بعدة قيم ثابتة، وهي الصيغة البسيطة (simple):
يُقيَّم التعبير الذي يلي CASE مرّة واحدة، ثم يُقارَن باستخدام = مع كل قيمة WHEN. هذه الصيغة أنظف حين تتعامل مع مقارنات تساوي على عمود واحد.
لكن انتبه لنقطة مهمة: صيغة simple CASE تستخدم =، وفي SQL القيمة NULL = NULL ليست صحيحة. فإذا كان العمود status قد يحتوي على NULL، فلن تتطابق معه أيٌّ من الفروع 'A' أو 'B' أو 'C'، وستذهب القيمة مباشرةً إلى ELSE. وللتعامل مع NULL بشكل صريح، انتقل إلى صيغة searched واستخدم WHEN status IS NULL THEN ....
استخدام CASE داخل ORDER BY
الجملة ORDER BY تقبل أي تعبير، ولذلك يعمل CASE معها بسلاسة. هذا مفيد حين تريد ترتيبًا مخصّصًا لا يتبع الترتيب الأبجدي ولا الرقمي:
'high' < 'low' < 'medium' أبجديًا، وهذا ترتيب لا يفيدك في تصنيف الأولويات. لكن عند تحويل كل قيمة أولوية إلى رقم عبر CASE، تحصل على الترتيب المنطقي الذي تريده فعلًا. أما , id في النهاية فيضمن ترتيبًا ثابتًا عند تساوي القيم.
استخدام CASE داخل WHERE
يمكنك وضع CASE داخل WHERE، لكن في أغلب الحالات لن تحتاج إلى ذلك، إذ إن سلسلة AND/OR تكون أوضح. الفائدة الحقيقية تظهر حين يكون الشرط نفسه متوقفًا على قيمة أخرى:
المنتجات المخفّضة تتأهّل عند سعر أقل من 20، أمّا العادية فعند أقل من 30. لاحظ أنّ الحدّ نفسه مشروط. بدون CASE كنت ستضطر لكتابة (on_sale = 1 AND price < 20) OR (on_sale = 0 AND price < 30) — نفس النتيجة، لكن مع ضوضاء أكثر.
استخدام CASE داخل دوال التجميع
هنا يُظهر CASE قيمته الحقيقية. اجمعه مع SUM أو COUNT لحساب إجماليات على مجموعة فرعية من الصفوف في تمريرة واحدة — وهو ما يُعادل في SQL سؤال "كم صفًّا منها يطابق الشرط؟":
يُرجِع CASE القيمة 1 للصفوف المطابِقة و0 لما عداها، فيتحوّل SUM إلى عدّ مشروط. ونفس الحيلة تنفع مع الإيرادات — أرجع total للصفوف المطابِقة و0 لغيرها. مسح واحد للجدول، وعدة تجميعات مشروطة في وقت واحد.
دالة IIF في SQLite: اختصار الفرعين
عندما يكون لديك شرط واحد ونتيجتان فقط، يوفّر لك SQLite الدالة IIF(cond, when_true, when_false). وهي ليست سوى اختصار مباشر لِـ CASE WHEN cond THEN when_true ELSE when_false END:
استخدم IIF عندما يكون المنطق ثنائيًا ويبدو أوضح في سطر واحد. أما إذا كان لديك ثلاثة فروع أو أكثر، أو احتجت للتعامل مع NULL بشكل منفصل، أو أردت الاستفادة من ترتيب التحقق المتسلسل لعدة جمل WHEN، فانتقل إلى CASE.
أخطاء شائعة يجب الانتباه لها
هناك بعض الأمور التي يقع فيها الكثيرون:
- نسيان
END. الكلمةCASEتفتح كتلة، وENDهي التي تُغلقها. وإن نسيتها، سيعطيك SQLite خطأ في التحليل بعيدًا عن موضع الخطأ الحقيقي، مما يصعّب تتبّعه. - غياب
ELSEيعنيNULL. إذا لم يتطابق أي فرع من فروعWHENوأهملت كتابةELSE، ستكون النتيجةNULL. أحيانًا يكون هذا هو المطلوب، لكن في الغالب لا. - ترتيب الفروع مهم. في الصيغة المبحوث عنها (searched)، يفوز أول فرع
WHENيتحقق شرطه. فلو وضعتWHEN total < 500قبلWHEN total < 100، فلن يصل التنفيذ أبدًا إلى الفرع الثاني. - اختلاط الأنواع. يمكن لكل فرع أن يُرجع نوعًا مختلفًا دون أن يعترض SQLite، لكن الكود الذي يستهلك النتيجة قد يتعطّل. حاول أن تجعل جميع الفروع تُرجع أنواعًا متوافقة (كلها نصية، أو كلها رقمية).
CASEالبسيط معNULL. كما ذكرنا سابقًا: الصيغة البسيطة تعتمد على=، وهذا المعامل لا يتطابق أبدًا معNULL. لذا استخدم الصيغة المبحوث عنها (searched) عندما تكون القيم الفارغة محتملة في بياناتك.
التالي: دوال السلاسل النصية
CASE يمنحك القدرة على التفرّع بناءً على القيم، أما الفصل القادم فسيبدأ معك في تحويل القيم. دوال السلاسل النصية مثل UPPER وLOWER وSUBSTR وREPLACE، إلى جانب أنماط LIKE، هي أدواتك اليومية لتنظيف الأعمدة النصية وإعادة تنسيقها. هذا ما سنراه في الفصل التالي.
الأسئلة الشائعة
ما هو تعبير CASE في SQLite؟
تعبير CASE هو نظير جملة if/else في لغة SQL؛ يقيّم مجموعة شروط ويُرجع قيمة بناءً عليها. الأهم أنه تعبير وليس جملة (statement)، أي يمكنك استخدامه في أي موضع يقبل قيمة: داخل SELECT وWHERE وORDER BY وUPDATE، وحتى داخل دوال التجميع. كل فرع يأخذ الشكل WHEN condition THEN value، مع إمكانية إضافة ELSE في النهاية.
ما الفرق بين الصيغة المبسطة والبحثية لتعبير CASE في SQLite؟
الصيغة المبسطة (simple CASE) تقارن تعبيرًا واحدًا بعدة قيم، مثل: CASE status WHEN 'A' THEN ... WHEN 'B' THEN ... END. أما الصيغة البحثية (searched CASE) فتقيّم شرطًا منطقيًا مستقلًا في كل فرع، مثل: CASE WHEN price > 100 THEN ... WHEN qty = 0 THEN ... END. الصيغة البحثية أكثر مرونة لأنها تتيح خلط الأعمدة والعمليات والتحقق من NULL بحرية.
متى أستخدم IIF بدلًا من CASE في SQLite؟
الدالة IIF(cond, a, b) ما هي إلا اختصار للتعبير CASE WHEN cond THEN a ELSE b END. استخدم IIF عندما يكون لديك شرط ثنائي بسيط لأنه أوضح في القراءة. أما إذا كان لديك ثلاثة فروع أو أكثر، أو احتجت ترتيبًا متسلسلًا للشروط، أو أردت التعامل مع NULL صراحةً عبر WHEN col IS NULL، فاللجوء إلى CASE هو الخيار الصحيح.