جملة WHERE بالتفصيل
الشيفرة موجودة لتشرح التعليقات للحاسوب. —Andy Harris
في جملة SELECT مثل:
SELECT code, coordinator
FROM course
WHERE coordinator = 'u0012047';
تُنفَّذ جملة FROM أولًا على يد خادم قاعدة البيانات. ويُحمَّل جدول "course" بأكمله في ذاكرة العمل. ونحن نعلم: نحن نكرر أنفسنا، لكن هذه معرفة أساسية حقًا إن كنت تريد أن تكون قادرًا على كتابة استعلامات جيدة.
ثم تُنفَّذ جملة WHERE (إن وُجدت). وتُبقى فقط الصفوف التي تحقق الشرط في WHERE. وتختفي جميع الصفوف الأخرى من الذاكرة. وبعد ذلك يُنفَّذ SELECT. وفي هذا الفصل، سننظر عن قرب في بعض إمكانات WHERE.
يمكنك اختبار أمثلة الشيفرة في جدول "course" في المخطط "ucllcatalogue" (أو في نسختك الخاصة من هذا الجدول في مخططك الشخصي).
معاملات المقارنة القياسية
توفّر SQL معاملات المقارنة القياسية: = (يساوي)، و<> أو != (لا يساوي)، و> (أكبر من)، و= (أكبر من أو يساوي) و<= (أصغر من أو يساوي). ويمكنك مقارنة الأعداد والسلاسل والتواريخ أو الطوابع الزمنية. ويعرض الاستعلام التالي جميع المقررات التي تاريخ بدايتها قبل 5 أبريل 2017:
SELECT *
FROM course
WHERE start_date < '2017-04-05'; --input date in ISO-format with single quotes
والتاريخ الأبعد في الماضي أصغر من التاريخ اللاحق، كما تتوقع منطقيًا.
ومقارنة السلاسل أعقد قليلًا. وللأسف فالنتيجة خاصة بكل قاعدة بيانات. فأن 'a' < 'b' أمر بديهي، ولكن ماذا عن مقارنة 'a' و'A'؟ أتدري؟ لنجربه ببساطة في PostgreSQL:
SELECT 'a' < 'A';
والنتيجة true. ونستنتج: في PostgreSQL تأتي الحروف الصغيرة قبل الحروف الكبيرة. وقد يكون الأمر بالعكس في نظام إدارة قواعد بيانات آخر.
وماذا عن المقارنة بين 'magic' و'monkey' وبين 'cent' و'century'؟ لنجرّب هذا:
SELECT 'magic' > 'monkey';
SELECT 'cent' > 'century';
والنتيجة false مرتين. فالمقارنة بين سلسلتين تجري في الواقع حرفًا حرفًا من اليسار إلى اليمين. وفي 'magic' و'monkey' الحرف الأول واحد، لكن الثاني لا. فـ 'a' الثانية في 'magic' أصغر (أبجديًا) من 'o' في 'monkey'، وبالتالي 'magic' < 'monkey'.
وكلمة 'cent' أصغر من 'century'. فتُقارن الحروف واحدًا واحدًا. وبعد أربعة حروف متطابقة، لا يوجد حرف خامس في سلسلة 'cent'، وبالتالي فهذه الكلمة أصغر (أقصر) من الكلمة الأطول 'century'.
IS (NOT) NULL
تعني القيمة NULL في حقل ما "غير معروف": فلا توجد قيمة لهذا الحقل. ومن الممكن الاختبار على قيمة NULL الخاصة هذه. لكن عليك فقط أن تتذكر ألا تستخدم = بل IS. ويعرض الاستعلام التالي قائمة بجميع المقررات التي لا نعرف بعد متى ستتوقف:
SELECT *
FROM course
WHERE end_date IS NULL; -- so don't write = NULL
وبالعكس، يعيد الاستعلام التالي قائمة بجميع المقررات التي لها تاريخ انتهاء فعلًا (وبالتالي لم تعد تُدرَّس أو يُعرف بالفعل إلى متى ستبقى هذه المقررات في المنهج):
SELECT *
FROM course
WHERE end_date IS NOT NULL;
أنماط السلاسل بواسطة LIKE
أحيانًا لا تريد البحث عن القيمة الكاملة لسلسلة، بل عن جزء منها فقط. افترض أنك تريد قائمة بجميع المقررات التي توجد فيها كلمة 'web' في مكان ما من الاسم (لا يهم إن كانت 'web' بحروف كبيرة أم صغيرة). ونعرف بالفعل من فصل سابق أن الدالة lower() ستكون مفيدة بالتأكيد في هذا الاستعلام لتحويل جميع الحروف الكبيرة إلى صغيرة. لكن كيف نختبر وجود 'web' في أي مكان من الاسم؟
ويمكن فعل ذلك بطرق مختلفة (مثلًا بـالتعابير النمطية). وهنا ننظر في أبسط طريقة باستخدام LIKE ونمط. وفي ذلك النمط يمكنك استخدام محرفين خاصين:
%: صفر أو أكثر من المحارف؛_(شرطة سفلية): محرف واحد بالضبط.
ويبحث الاستعلام التالي في جميع الأسماء التي تحتوي السلسلة الفرعية 'web' (لا تهمّ حالة الأحرف):
SELECT code, name, semester
FROM course
WHERE lower(name) LIKE '%web%';
جرّب الآن التمارين التالية.
صِف بالكلمات عمّا تبحث أنماط LIKE التالية:
... LIKE 'B%'
... LIKE '_e%'
... LIKE '%e%e%e%'
... LIKE '__a_b%'
الحل
... LIKE 'B%' --all strings beginning with capital B
... LIKE '_e%' --all strings with an e as second letter
... LIKE '%e%e%e%' --all strings with at least 3 times the letter e
... LIKE '__a_b%' --all strings with a as third and b as fifth letter
اكتب استعلام SQL يولّد قائمة بجميع المحاضرين الذين ينتهي رمز محاضرهم بالرقم 4. وأضف أيضًا عمودًا باسم المقرر المسؤولين عنه.
الحل
SELECT coordinator, name
FROM course
WHERE coordinator like '%4';
-- It's also possible to use substring(), please try this version as well
الاحتفاظ بالصفوف التي لا تحقق شرطًا
بشرط في جملة WHERE، نُبقي فقط الصفوف التي تحقق ذلك الشرط. فماذا لو أردت جميع الصفوف الأخرى، أي فقط تلك التي لا تحقق الشرط؟ لهذا الغرض يمكنك استخدام الكلمة المفتاحية NOT. ويعيد الاستعلام التالي جميع المقررات التي لا تحقق الشرط، أي تلك التي لا تبدأ بالحروف 'Web':
SELECT code, name, semester
FROM course
WHERE NOT name LIKE 'Web%';
اسرد جميع المقررات (الرمز والاسم وعدد النقاط) التي لا يحتوي اسمها على حرف 'a' واحد (الحروف الصغيرة فقط، ويجوز أن يظهر الحرف الكبير 'A'). اكتب الاستعلام. فماذا تعدّل في حلك إذا كان الحرف الكبير 'A' ممنوعًا أيضًا؟
الحل
SELECT code, name, credits
FROM course
WHERE NOT name like '%a%'; -- adaptation: NOT lower(name) like '%a%'
ربط الشروط بـ AND وOR
يمكننا ربط عدة شروط بـ AND وOR:
A AND B:TRUEفقط إذا كان A وB معًاTRUE؛A OR B:TRUEإذا كان A أو B أو كلاهماTRUE.
ويمكن بالطبع استخدام AND وOR أيضًا مع NOT.
فماذا يفعل الاستعلام التالي:
SELECT *
FROM course
WHERE language = 'nl' AND lower(name) LIKE '%prog%';
الحل
يعرض هذا الاستعلام جميع أعمدة المقررات المدرَّسة بالهولندية والتي يحتوي اسمها (المحوَّل إلى حروف صغيرة) على السلسلة 'prog'. ويجب أن يتحقق الشرطان معًا في الوقت نفسه!
اكتب الاستعلام الذي يولّد قائمة بجميع المقررات المدرَّسة بالهولندية أو الفرنسية.
الحل
SELECT *
FROM course
WHERE language = 'nl' OR language = 'fr'; -- we will see how to write this query more concisely later
ويمكنك ربط أكثر من شرطين. وعليك أن تكون حذرًا كما يوضح المثال التالي. اسرد جميع المقررات المدرَّسة بالهولندية أو الفرنسية والتي لا تزال تُدرَّس. نجرّب الاستعلام التالي:
SELECT *
FROM course
WHERE language = 'nl' OR language = 'fr' AND end_date IS NULL;
يعيد هذا الاستعلام نتيجة خاطئة. نفّذ الاستعلام واشرح لماذا هذه النتيجة غير صحيحة.
الحل
السبب متعلق بترتيب العمليات. فكما تعلمت منذ زمن طويل أن 3 + 4 * 2 ينبغي أن تُقرأ 3 + (4 * 2) لأن الضرب يسبق الجمع، يوجد أيضًا ترتيب للمعاملات في WHERE.
وترتيب المعاملات، من الأولوية العليا إلى الدنيا:
- أولًا معاملات المقارنة مثل
=و`` و<=..., - ثم
NOT, - ثم
AND, - وأخيرًا وليس آخرًا
OR.
وهذا يعني أن الشرط أعلاه لا يُقيَّم ببساطة من اليسار إلى اليمين بل على النحو language = 'nl' OR (language = 'fr' AND end_date IS NULL). ويمكنك ترجمة هذا الشرط إلى "جميع المقررات الهولندية (حالية كانت أم سابقة، لا يهم) أو جميع المقررات الفرنسية التي لا تزال تُدرَّس". وهذا ليس ما أردناه.
والحل بسيط: استخدم الأقواس. ويصبح الاستعلام كما قصدناه:
SELECT *
FROM course
WHERE (language = 'nl' OR language = 'fr') AND end_date IS NULL;
وبالنسبة إلى الشروط المركّبة المعقدة، فإن الأقواس فكرة جيدة دائمًا لأنها تزيد قابلية قراءة الشرط.
BETWEEN
نبدأ بتمرين بسيط:
أعطِ جميع المقررات (الرمز والاسم والنقاط) التي لها من 3 إلى 8 نقاط.
الحل
SELECT code, name, credits
FROM course
WHERE credits >=3 AND credits <=8;
وتوفّر SQL تدوينًا أقصر قليلًا لهذا النوع من الاستعلامات (وأكثر اتساقًا مع اللغة العادية):
SELECT code, name, credits
FROM course
WHERE credits BETWEEN 3 AND 8; -- both boundaries are included
وإذا كان عليك كتابة استعلامات SQL في امتحان، فيجوز لك دائمًا أن تختار بين تدوين BETWEEN أو النسخة الأطول قليلًا. ويجب أن تفهم التدوينين معًا، بوضوح.
IN
تتيح لك الكلمة المفتاحية IN اختصار بعض الشروط. ومرة أخرى، تمرين صغير للبداية:
اكتب الاستعلام الذي يعرض جميع المقررات التي يكون منسّقها أحد الأشخاص التالية أسماؤهم: u0012047 وu0015584 وu0024689 وu0031447.
الحل
SELECT code, name, coordinator
FROM course
WHERE coordinator = 'u0012047' OR coordinator = 'u0015584' OR
coordinator = 'u0024689' OR coordinator = 'u0031447';
مرهق بعض الشيء، أليس كذلك؟ يمكن اختصار هذا الاستعلام بـ IN:
SELECT code, name, coordinator
FROM course
WHERE coordinator IN ('u0012047','u0015584','u0024689','u0031447');
نظرة عامة على المعاملات
يعطي الجدول التالي نظرة عامة على المعاملات التي يمكنك استخدامها في جملة WHERE.
| العامل | الوظيفة | المثال |
|---|---|---|
= |
يساوي | WHERE name = 'Jan Van Hee' |
<> أو != |
لا يساوي | WHERE year <> 2023 |
> |
أكبر من | WHERE amount > 250 |
< |
أصغر من | WHERE amount < 400 |
>= |
أكبر من أو يساوي | WHERE amount >= 250 |
<= |
أصغر من أو يساوي | WHERE amount <= 400 |
BETWEEN |
ضمن نطاق | WHERE amount BETWEEN 250 AND 400 |
IN |
يساوي واحدة من مجموعة قيم | WHERE city IN ('Leuven', 'Brussel', 'Gent') |
LIKE |
يطابق نمطًا (بـ '%' من '_'، حساس لحالة الأحرف) | WHERE name LIKE 'Jan%' |
ILIKE |
يطابق نمطًا (غير حساس لحالة الأحرف) | WHERE name ILIKE 'jan%' |
NOT |
ينفي شرطًا | WHERE name NOT LIKE 'Jan%' |
تمارين SQLzoo
SQLzoo منصة تدريب تفاعلية. تُدخل شيفرة SQL يراجعها النظام لك. ومن الجيد تجربة هذه التمارين. وبهذه الطريقة ستتعلم العمل بجداول غير التي نستخدمها في نص هذه الدورة.
جدول 'world'
يحتوي هذا الجدول البيانات التالية عن البلدان:
- name
- continent
- area
- population
- gdp
وكمقدمة إلى هذا الجدول، أنجز سلسلة التمارين 1 بثلاثة تمارين صغيرة. وهنا تستخدم WHERE وIN وBETWEEN (وبالطبع SELECT وFROM).
أنجز الاختبار 1 بسبعة أسئلة تركز على WHERE.
سلسلة التمارين 2 على جدول 'world' تحتوي 13 تمرينًا على غرار السابق، لكن مع دوال حسابية إضافية مثل ROUND وبعض معالجة السلاسل.
الاختبار 2 يحتوي مرة أخرى 7 أسئلة. انتبه إلى التفاصيل!