جملة WHERE في SQLite: تصفية الصفوف صفًّا صفًّا
عندما تستخدم SELECT بدون WHERE، فإنك تحصل على كل صفوف الجدول، وهذا نادرًا ما يكون ما تحتاجه فعلًا. هنا يأتي دور جملة WHERE في SQLite، إذ تُمكّنك من الاحتفاظ بالصفوف التي تُحقّق شرطًا معيّنًا فقط؛ فيمر SQLite على الجدول صفًّا صفًّا، ويُقيّم الشرط على كل صف، ويُبقي الصفوف التي يتحقّق فيها الشرط.
يرجع الاستعلام ثلاثة صفوف فقط: Neuromancer وHyperion وThe Martian. تم اختبار الشرط year > 1980 على كل صف، ولم يَعبُر سوى الصفوف التي حقّقت الشرط.
التصوّر الذهني الأبسط: جملة WHERE هي بمثابة مِصفاة تقف بين FROM والأعمدة التي تختارها في SELECT. كل صف ينتج عنه true يمر، وما عدا ذلك يُستبعد.
عوامل المقارنة في SQLite
العوامل الأساسية تعمل تماماً كما تتوقّع:
= للمساواة، و != أو <> بمعنى "لا يساوي"، أما < و <= و > و >= فللمقارنة الترتيبية. ومقارنة النصوص تستخدم نفس المعاملات — فمثلاً author = 'Asimov' يطابق القيمة حرفاً بحرف بشكل تام.
نقطة مهمة ينبغي الانتباه لها: لغة SQL تستخدم علامات الاقتباس المفردة للنصوص الحرفية، بينما علامات الاقتباس المزدوجة مخصصة للمعرّفات (أسماء الأعمدة أو الجداول). قد تعمل WHERE author = "Asimov" في SQLite لأسباب تاريخية، لكنها غير قابلة للنقل بين أنظمة قواعد البيانات، وقد تتصرف بشكل خاطئ بصمت إذا صادف أن "النص" يطابق اسم عمود ما. التزم دائماً بعلامات الاقتباس المفردة.
استخدام AND و OR والأقواس في SQLite
في الاستعلامات الواقعية، عادةً ما نحتاج إلى دمج عدة شروط معاً. المعامل AND يشترط تحقّق الطرفين معاً، أما OR فيكفي معه تحقّق أحد الطرفين على الأقل:
الاستعلام الأول يجيب الكتب الحديثة والقصيرة في نفس الوقت، أما الثاني فيجيب الكتب لأيٍّ من المؤلفَين.
عند خلط AND وOR معًا، أولوية المعاملات تُوقع كثيرين في الفخ. الـ AND أقوى ارتباطًا من OR، يعني:
تُقرأ هذه العبارة على أنها Herbert OR (Gibson AND year > 1980) — أي كل كتب Herbert بغضّ النظر عن السنة، إضافةً إلى كتب Gibson الصادرة بعد 1980. غالبًا هذا ليس ما تقصده. لذا ضَع نيّتك بين قوسين:
عند الشك، استخدم الأقواس. مُحسِّن الاستعلامات لا يكترث لها، لكن الشخص الذي سيقرأ الكود بعدك سيشكرك عليها.
التعامل مع NULL في SQLite: NULL ليس قيمة عادية
هذا هو الفخ الذي يقع فيه الجميع مرة واحدة على الأقل عند كتابة جملة WHERE. كلمة NULL في SQL تعني "قيمة مجهولة"، والمجهول لا يمكن مقارنته. عبارة column = NULL _ليست_ خاطئة (false)، بل نتيجتها NULL، وجملة WHERE تتعامل مع هذه النتيجة على أنها "تجاهل هذا الصف".
IS NULL و IS NOT NULL هما المُعاملان الوحيدان اللذان يفحصان NULL فحصًا مباشرًا. احفظ هذه القاعدة جيدًا — أي مقارنة أخرى مع NULL ستُرجع NULL، وسيتم استبعاد الصفوف بصمت دون أن تشعر.
نفس القاعدة تنطبق على النفي. الجملة WHERE author != 'Asimov' لن تُرجع الصفوف التي يكون فيها author IS NULL، لأن NULL != 'Asimov' نتيجتها أيضًا NULL. إذا أردت تضمين قيم NULL، فاطلبها صراحةً: WHERE author != 'Asimov' OR author IS NULL.
IN و BETWEEN: اختصارات ستستخدمها يوميًا
يتحقق IN من وجود القيمة ضمن قائمة. وهو طريقة أنظف لكتابة سلسلة من OR:
BETWEEN يتحقّق من نطاق معيّن، ويشمل الطرفين معًا:
year BETWEEN 1980 AND 2000 تكافئ تمامًا year >= 1980 AND year <= 2000، لكنها أقصر وأوضح. انتبه فقط لنقطة واحدة: كلا الطرفين مُضمَّنان في النطاق. إذا أردت استبعاد الطرفين، اكتب المقارنات صراحةً.
ملاحظة سريعة حول IN والقيم NULL: الشرط WHERE column NOT IN (1, 2, NULL) لن يُرجع أي صف على الإطلاق، لأن أي مقارنة مع NULL تُنتج NULL. لذا استبعِد قيم NULL من القائمة، أو تعامل معها بشكل منفصل عبر IS NULL.
مطابقة الأنماط باستخدام LIKE في SQLite
تُستخدم LIKE لمطابقة الأنماط النصية، وتعتمد على رمزين بديلين:
%يطابق أي تسلسل من الأحرف (بما في ذلك تسلسل فارغ)._يطابق حرفًا واحدًا فقط.
بشكل افتراضي، عامل LIKE في SQLite لا يفرّق بين الحروف الكبيرة والصغيرة في أحرف ASCII — فمثلاً 'Dune' LIKE 'dune' نتيجته true. وهذا أمر غير متوقع إن كنت قادماً من Postgres، حيث يفرّق LIKE بين حالة الأحرف ويوجد ILIKE للمطابقة بدون التفريق بينها. (SQLite لا يدعم ILIKE أصلاً.)
إذا كنت بحاجة إلى مطابقة تراعي حالة الأحرف، فأمامك خياران. إمّا تفعيل البراغما العامة:
PRAGMA case_sensitive_like = ON;
أو استخدم GLOB، التي تكون دائمًا حساسة لحالة الأحرف وتعتمد على رموز البدل بأسلوب Unix (* لأي تسلسل، و? لحرف واحد):
GLOB 'd*' لن يطابق أي شيء هنا، فحالة الأحرف مهمة.
تصفية الصفوف في SQLite حسب التاريخ
يخزّن SQLite التواريخ كنصوص (عادةً بصيغة YYYY-MM-DD أو صيغة ISO 8601 الكاملة)، ما يعني أن المقارنة النصية تعمل أيضًا كمقارنة تواريخ، شرط الالتزام بصيغة ISO:
بما أن '2024-06-01' < '2024-11-08' صحيحة سواء قارنّاها كنصوص أو كتواريخ، فإن هذه الاستعلامات تعمل كما تتوقع تماماً. لكن إذا خزّنت التواريخ بأي صيغة أخرى مثل ('15/01/2024' أو 'Jan 15 2024')، فستعطيك المقارنات نتائج خاطئة دون أي تحذير. التزم دائماً بصيغة ISO 8601، وستشكر نفسك لاحقاً.
أما إذا احتجت إلى عمليات أعقد على التواريخ (كاستخراج السنة أو المقارنة مع تاريخ "اليوم")، فإن SQLite يوفر لك دوال مثل date() وstrftime() وjulianday()، وسنتناولها بالتفصيل في فصل التاريخ والوقت.
نجمع كل ما سبق معاً
إليك استعلاماً يجمع بين عدة أدوات مما تعلمناه دفعة واحدة:
اقرأه سطرًا سطرًا: احتفظ بالصفوف التي لها سنة معروفة، وضمن النطاق المطلوب، ومن أحد المؤلفَين أو ذات طول كافٍ، وليست مسودّة. هذا بالضبط ما تجيده جملة WHERE في SQLite: دمج شروط صغيرة وواضحة في مرشّح دقيق لتصفية الصفوف.
عادتان يستحسن التمسّك بهما:
- ضع كل شرط في سطر مستقل مع إزاحة مناسبة. جملة
WHEREالطويلة تصبح غير قابلة للقراءة بسرعة إذا كُتبت في سطر عملاق واحد. - علّق على القصد حين لا يكون الشرط بديهيًا. تعليق بسيط مثل
-- استبعاد المسودّاتيوفّر عليك الكثير لاحقًا.
الخطوة التالية: المعاملات و NULL بتفصيل أكبر
جملة WHERE في جوهرها معاملات تُطبَّق على الأعمدة، وقيمة NULL تغيّر بهدوء سلوك كل معامل تقريبًا. الصفحة التالية تتعمّق في مجموعة معاملات SQLite — الحساب، ودمج النصوص بـ ||، وعائلة IS، والمنطق الثلاثي القيم — حتى تتوقف المفاجآت عن كونها مفاجآت.
الأسئلة الشائعة
كيف تعمل جملة WHERE في SQLite؟
تقوم WHERE بتصفية الصفوف الناتجة عن الاستعلام عبر اختبار شرط معيّن على كل صف. الصفوف التي يتحقق فيها الشرط (true) يتم الاحتفاظ بها، أما التي تكون نتيجتها false أو NULL فيتم استبعادها. تأتي مباشرة بعد FROM بهذا الشكل: SELECT ... FROM table WHERE condition.
كيف أجمع بين أكثر من شرط في جملة WHERE؟
استخدم AND وOR. عامل AND يتطلّب تحقق الطرفين معًا، بينما OR يكتفي بتحقق أحدهما. وبما أن AND أعلى أولوية من OR، يُفضّل استخدام الأقواس عند خلط الشروط لتجنّب أي لبس، مثل: WHERE (a OR b) AND c.
لماذا لا تعمل WHERE column = NULL في SQLite؟
لأن NULL تعني "قيمة غير معروفة"، فإن أي مقارنة باستخدام = أو != ستُرجع NULL بدلاً من true أو false، والصفوف لا تُحفظ إلا عندما يكون الشرط محققًا فعلًا. لذلك استخدم IS NULL وIS NOT NULL، فهما العاملان الوحيدان القادران على فحص قيمة NULL مباشرة.
هل LIKE في WHERE حساسة لحالة الأحرف في SQLite؟
افتراضيًا، LIKE غير حساسة لحالة الأحرف بالنسبة للحروف الإنجليزية (ASCII)، فمثلًا 'Hello' LIKE 'hello' يُرجع true. إذا أردت جعلها حساسة للحالة، فعّل PRAGMA case_sensitive_like = ON; أو استخدم GLOB بدلًا منها، فهي حساسة دائمًا للحالة وتعتمد رموز ويلدكارد بنمط يونكس (* و?).
ما الفرق بين IN و BETWEEN في جملة WHERE؟
IN تتحقق من تطابق القيمة مع أي عنصر داخل قائمة محددة، مثل WHERE id IN (1, 2, 3). أما BETWEEN فتختبر إن كانت القيمة تقع ضمن نطاق متصل (شامل للطرفين)، مثل WHERE age BETWEEN 18 AND 30. استخدم IN للقيم المنفصلة، وBETWEEN للنطاقات الرقمية أو التواريخ.