الدوال و CASE في SQL: معالجة البيانات داخل الاستعلام
حتى الآن استعلاماتك تعرض البيانات كما هي مخزنة. لكن التقارير الحقيقية تحتاج أكثر: اسم بحروف كبيرة، ورقم مقرَّب، وكلمة «غائب» بدل خانة فارغة، وتقدير «ممتاز» بدل الدرجة 92. في هذا الدرس ستتعلم الدوال (Functions) التي تعالج القيم داخل الاستعلام، وتعبير CASE الذي يضيف منطق «إذا… وإلا» إلى SQL.
سنعمل على جدولي students و courses كما في الدروس السابقة.
دوال النصوص
Section titled “دوال النصوص”SELECT title, UPPER(title) AS upper, LOWER(title) AS lower, LENGTH(title) AS lenFROM courses;الناتج:
| title | upper | lower | len |
|---|---|---|---|
| Python | PYTHON | python | 6 |
| HTML | HTML | html | 4 |
| SQL | SQL | sql | 3 |
الدالة تأخذ قيمة من كل صف وتُرجع قيمة جديدة. والبيانات في الجدول لا تتغير: التحويل يحدث في نتيجة الاستعلام فقط.
SELECT title, SUBSTR(title, 1, 2) AS short, REPLACE(title, 'L', 'l') AS replacedFROM courses;الناتج:
| title | short | replaced |
|---|---|---|
| Python | Py | Python |
| HTML | HT | HTMl |
| SQL | SQ | SQl |
| الدالة | وظيفتها |
|---|---|
UPPER و LOWER |
تحويل لحروف كبيرة أو صغيرة |
LENGTH |
عدد الحروف |
SUBSTR(نص, بداية, طول) |
جزء من النص، والترقيم يبدأ من 1 لا من صفر |
REPLACE(نص, قديم, جديد) |
استبدال جزء بآخر |
TRIM |
حذف المسافات من الطرفين |
من أشهر استخداماتها البحث دون حساسية لحالة الحروف: WHERE LOWER(email) = 'sara@example.com' يجد البريد مهما كُتب بحروف كبيرة أو صغيرة.
دوال الأرقام
Section titled “دوال الأرقام”SELECT name, score, score / 10 AS int_div, score / 10.0 AS real_div, ROUND(score / 3.0, 2) AS thirdFROM students WHERE id <= 2;الناتج:
| name | score | int_div | real_div | third |
|---|---|---|---|---|
| سارة | 92 | 9 | 9.2 | 30.67 |
| عمر | 78 | 7 | 7.8 | 26.0 |
ROUND(قيمة, خانات): تقريب إلى عدد محدد من الخانات العشرية.ABS(قيمة): القيمة المطلقة، و%باقي القسمة.
COALESCE: بديل القيمة الفارغة
Section titled “COALESCE: بديل القيمة الفارغة”تذكّر أن ليلى بلا درجة، وأن أي عملية حسابية مع NULL نتيجتها NULL. الدالة COALESCE تُرجع أول قيمة غير فارغة من القيم التي تعطيها لها:
SELECT name, score, COALESCE(score, 0) AS safe_score, score + 5 AS bonusFROM students WHERE id >= 4;الناتج:
| name | score | safe_score | bonus |
|---|---|---|---|
| يوسف | 64 | 64 | 69 |
| ليلى | NULL | 0 | NULL |
| أحمد | 71 | 71 | 76 |
COALESCE(score, 0) تعني: «هات الدرجة، وإذا كانت فارغة فهات صفراً». وهي مثل اللاعب الاحتياطي: ينزل الملعب فقط إذا غاب الأساسي.
CASE: إذا… وإلا
Section titled “CASE: إذا… وإلا”CASE تفحص شروطاً بالترتيب، وتُرجع قيمة أول شرط يتحقق:
SELECT name, score, CASE WHEN score >= 85 THEN 'ممتاز' WHEN score >= 70 THEN 'جيد' WHEN score IS NULL THEN 'غائب' ELSE 'يحتاج دعماً' END AS levelFROM students;الناتج:
| name | score | level |
|---|---|---|
| سارة | 92 | ممتاز |
| عمر | 78 | جيد |
| منى | 85 | ممتاز |
| يوسف | 64 | يحتاج دعماً |
| ليلى | NULL | غائب |
| أحمد | 71 | جيد |
- كل
WHEN شرط THEN قيمةفرع، والفروع تُفحص من الأعلى للأسفل. سارة تحقق الشرطين الأولين، لكنها أخذت «ممتاز» لأنه الأول. ELSE: القيمة إذا لم يتحقق أي شرط. بدونها تكون النتيجةNULL.END: نهاية التعبير، وبعدها اسم العمود بـAS.
تخيّل CASE مثل موظف الفرز في البريد: ينظر إلى كل طرد، ويضعه في أول صندوق تنطبق عليه القاعدة.
وإذا كانت كل الفروع تقارن العمود نفسه بقيم محددة، فهناك صيغة أقصر:
SELECT name, CASE city WHEN 'القاهرة' THEN 'العاصمة' ELSE 'خارج العاصمة' END AS regionFROM students WHERE id <= 3;الناتج:
| name | region |
|---|---|
| سارة | العاصمة |
| عمر | خارج العاصمة |
| منى | خارج العاصمة |
CASE مع دوال التجميع
Section titled “CASE مع دوال التجميع”هنا تظهر قوة CASE الحقيقية: عدّ عدة أشياء مختلفة في استعلام واحد.
SELECT COUNT(*) AS total, SUM(CASE WHEN score >= 75 THEN 1 ELSE 0 END) AS passed, SUM(CASE WHEN score < 75 THEN 1 ELSE 0 END) AS failed, SUM(CASE WHEN score IS NULL THEN 1 ELSE 0 END) AS absentFROM students;الناتج:
| total | passed | failed | absent |
|---|---|---|---|
| 6 | 3 | 2 | 1 |
الحيلة: CASE تحوّل كل صف إلى 1 أو 0، و SUM تجمع الآحاد. بدونها كنت ستحتاج ثلاثة استعلامات منفصلة.
ويمكن التجميع حسب ناتج CASE نفسه:
SELECT CASE WHEN age >= 15 THEN 'كبار' ELSE 'صغار' END AS age_group, COUNT(*) AS total, ROUND(AVG(score), 1) AS averageFROM studentsGROUP BY age_group;الناتج:
| age_group | total | average |
|---|---|---|
| صغار | 4 | 82.7 |
| كبار | 2 | 71.0 |
CAST: تحويل النوع
Section titled “CAST: تحويل النوع”SELECT CAST('42' AS INTEGER) + 1 AS n, CAST(3.9 AS INTEGER) AS cut, CAST(85 AS TEXT) || '%' AS pct;الناتج:
| n | cut | pct |
|---|---|---|
| 43 | 3 | 85% |
CAST(قيمة AS نوع) تحوّل القيمة من نوع لآخر. لاحظ أن التحويل إلى عدد صحيح يقصّ الكسر ولا يقرّب: 3.9 أصبحت 3.
جرّب بنفسك
Section titled “جرّب بنفسك”- اعرض اسم كل كورس بحروف صغيرة مع عدد حروفه، مرتبة من الأطول للأقصر.
- اعرض اسم كل طالب ودرجته من 10 (أي الدرجة مقسومة على 10.0)، مع
COALESCEلتظهر ليلى بصفر. - أضف عموداً
resultيعرض «ناجح» لمن درجته 70 فأكثر، و«راسب» للأقل، و«لم يُختبر» لمن بلا درجة. - تحدٍّ: في استعلام واحد، اعرض لكل مدينة عدد طلابها وعدد الحاصلين منهم على 80 فأكثر.
أخطاء شائعة
Section titled “أخطاء شائعة”- نسيان END: كل
CASEتنتهي بـEND. نسيانها يعطي خطأ صياغة غير واضح. - ترتيب الفروع: لو وضعت
WHEN score >= 70قبلWHEN score >= 85فلن يصل أحد إلى الفرع الثاني. ابدأ بالشرط الأخص. - مقارنة NULL داخل CASE:
WHEN score = NULLلا تتحقق أبداً. اكتبWHEN score IS NULL. - دالة على عمود في WHERE:
WHERE UPPER(city) = 'CAIRO'تعمل، لكنها تمنع استخدام الفهرس على العمود كما سترى في الدرس القادم.
اختبر نفسك
Section titled “اختبر نفسك”سؤال
ماذا تُرجع COALESCE(NULL, NULL, 7, 9)؟
تُرجع أول قيمة غير فارغة من اليسار، وهي 7.
سؤال
طالب درجته 90 في CASE WHEN score >= 70 THEN 'جيد' WHEN score >= 85 THEN 'ممتاز' END. ما النتيجة؟
الفروع تُفحص بالترتيب، وأول شرط يتحقق هو score >= 70، فلا يصل التنفيذ للفرع الثاني.
الخلاصة
Section titled “الخلاصة”- الدوال تعالج القيم في النتيجة دون تغيير البيانات المخزنة.
UPPERوLENGTHوSUBSTRوREPLACEللنصوص، وROUNDوABSللأرقام.- احذر قسمة الأعداد الصحيحة، و
COALESCEتعطي بديلاً للقيمة الفارغة. CASE WHEN ... THEN ... ELSE ... ENDمنطق شرطي يُفحص بالترتيب.SUM(CASE ...)تعدّ عدة فئات في استعلام واحد، وCASTتحوّل الأنواع.