انتقال للمقال

قراءة البيانات من الجداول باستخدام SELECT

السلام عليكم ورحمة الله وبركاته

وقت القراءة: ≈ 20 دقيقة (بمعدل فنجان واحد من الشاي 😊)

المقدمة

الآن حان الوقت لنتعلم أهم وأكثر الأوامر استخدامًا في الـ SQL وهو أمر الـ SELECT
وهو كأمر بسيط لكن قوي جدًا في نفس الوقت

هذا الأمر هو ما ستستخدمه 90% من الوقت في تعاملك مع قواعد البيانات
لأن معظم العمليات التي ستقوم بها ستكون قراءة البيانات من الجداول
ووظيفتك تكون كيف تكتب Query سريعة وتؤدي الغرض المطلوب منها
لأنك قد تكتب Query تقوم بقراءة بيانات من جدول كبير جدًا، وقد تستغرق وقتًا طويلًا
ثم يأتي شخص آخر يحقق لك نفس المطلوب لكن بـ Query أفضل وأسرع

لذلك من المهم جدًا أن تتعلم كيف تكتب Query فعالة وسريعة باستخدام أمر الـ SELECT
في هذه المقالة سنتعلم كل شيء عن أمر الـ SELECT من الأساسيات البسيطة إلى الاستخدامات المتقدمة

ما هو أمر الـ SELECT ؟

أمر الـ SELECT يستخدم لـ قراءة واسترجاع البيانات من الجداول في قاعدة البيانات
وكلمة Query تعني استعلام أو يمكنك أن تتخيل أن الـ Query في الأساس طريقة لـ طرح سؤال على قاعدة البيانات، وستقوم قاعدة البيانات بالإجابة عليك بالنتيجة المطلوبة
لذالك كلما سألت السؤال بطريقة صحيحة، كلما كانت الإجابة دقيقة وسريعة

بالتالي بافتراض أننا لدينا بيانات كثيرة من الطلاب في جدول الـ Students
ثم قلت لك أحضر لي أسماء جميع الطلاب
فستقوم بكتابة أمر الـ SELECT كالتالي:

SELECT name FROM Students;

هذا الأمر يعني "أريد قراءة عمود الاسم من جدول الطلاب" هكذا بكل بساطة

لنسأل سؤال آخر، لنفترض أنك تريد قراءة تريد أسماء وأعمار جميع الطلاب الذين أعمارهم أكبر من 20 سنة
فستكتب الأمر كالتالي:

SELECT name, age FROM Students WHERE age > 20;

حتى أنك حين تقرأ الـ Query هذه، ستشعر وكأنك تتحدث مع قاعدة البيانات بلغة بشرية بسيطة

  • SELECT name, age تعني "أريد قراءة عمود الاسم وعمود العمر"
  • FROM Students تعني "من جدول الطلاب"
  • WHERE age > 20 تعني "حيث العمر أكبر من 20"

هذا هو جوهر أمر الـ SELECT، وهو ما ستستخدمه في معظم تعاملاتك مع قواعد البيانات

ملحوظة: تذكر أن أوامر الـ SQL يمكننا كتابتها lowercase أو UPPERCASE أو حتى camelCase
لكن المتعارف عليه هو كتابة الأوامر بـ UPPERCASE للتفريق بينها وبين أسماء الجداول والأعمدة التي عادة ما تكون بـ lowercase أو camelCase
بالتالي في الـ Query يمكنك استنتاج بمجرد النظر أن SELECT و FROM و WHERE هي أوامر SQL
بينما name و age و Students هي أسماء أعمدة أو جداول
لذا يفضل كتابة الأوامر بـ UPPERCASE لتسهيل القراءة والفهم

تجهيز الجدول للأمثلة العملية

قبل أن نبدأ في تعلم أمر الـ SELECT والتطبيق عليه، دعونا نتفق على جدول موحد سنستخدمه في جميع الأمثلة
وهو نفس جدول الـ Students الذي استخدمناه في المقالات السابقة

CREATE TABLE Students (
    id INT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(100) NOT NULL,
    email VARCHAR(100) NOT NULL UNIQUE,
    age INT,
    level VARCHAR(50) DEFAULT 'Beginner'
);

ولنفترض أن لدينا البيانات التالية في جدول الـ Students:

+----+-----------------+-----+--------------+------------------------+
| id | name            | age | level        | email                  |
+----+-----------------+-----+--------------+------------------------+
| 1  | Ahmed Moustafa  | 20  | Beginner     | [email protected]   |
| 2  | Osama Ali       | 19  | Intermediate | [email protected]   |
| 3  | Mohamed Adel    | 22  | Advanced     | [email protected] |
| 4  | Kamal Mahmoud   | 21  | Beginner     | [email protected]   |
| 5  | Ayman Hassan    | 20  | Advanced     | [email protected]   |
+----+-----------------+-----+--------------+------------------------+

بالطبع لن اعيد شرح كيف نضيف البيانات إلى الجدول بإستخدام الـ INSERT، لأننا فعلنا ذلك في المقالات السابقة
الآن دعونا نبدأ في تعلم كيفية استخدام أمر الـ SELECT لقراءة هذه البيانات بطرق مختلفة

قراءة جميع البيانات من الجدول

أبسط استخدام لأمر الـ SELECT هو قراءة جميع البيانات من أي جدول:

SELECT * FROM Students;

هنا الرمز * يعني "جميع الأعمدة"
بالتالي هذا الأمر ببساطة يعني "أريد كل البيانات من جدول الطلاب"
وستكون النتيجة كالتالي:

+----+-----------------+-----+--------------+------------------------+
| id | name            | age | level        | email                  |
+----+-----------------+-----+--------------+------------------------+
| 1  | Ahmed Moustafa  | 20  | Beginner     | [email protected]   |
| 2  | Osama Ali       | 19  | Intermediate | [email protected]   |
| 3  | Mohamed Adel    | 22  | Advanced     | [email protected] |
| 4  | Kamal Mahmoud   | 21  | Beginner     | [email protected]   |
| 5  | Ayman Hassan    | 20  | Advanced     | [email protected]   |
+----+-----------------+-----+--------------+------------------------+

النتيجة ستكون جميع البيانات الموجودة في جدول الـ Students كما هو موضح في الجدول أعلاه

قراءة أعمدة محددة فقط

في حالة أنك تريد عرض بيانات من أعمدة محددة، يمكنك تحديد الأعمدة التي تحتاجها فقط
مثل name و age فقط، بدلاً من قراءة جميع الأعمدة
فستطيع تحديد الأعمدة التي تريدها بدلاً من استخدام *

مثلاً إذا كنت تريد أسماء الطلاب وأعمارهم فقط:

SELECT name, age FROM Students;

والنتيجة ستكون:

+-----------------+-----+
| name            | age |
+-----------------+-----+
| Ahmed Moustafa  | 20  |
| Osama Ali       | 19  |
| Mohamed Adel    | 22  |
| Kamal Mahmoud   | 21  |
| Ayman Hassan    | 20  |
+-----------------+-----+

لاحظ أننا حددنا فقط الأعمدة name و age
بالتالي تم عرض أسماء الطلاب وأعمارهم فقط

وإذا كنت تريد عمود واحد فقط، مثل أسماء الطلاب:

SELECT name FROM Students;

النتيجة:

+-----------------+
| name            |
+-----------------+
| Ahmed Moustafa  |
| Osama Ali       |
| Mohamed Adel    |
| Kamal Mahmoud   |
| Ayman Hassan    |
+-----------------+

ملحوظة: يفضل تحديد الأعمدة التي تحتاجها فقط بدلاً من استخدام *
لأن هذا يحسن الأداء ويقلل من حجم البيانات التي يتم سحبها ونقلها من قاعدة البيانات إلى التطبيق
وأيضًا يجعل النتائج أكثر وضوحًا وتعرف ماذا تحتاج بالضبط

تصفية البيانات باستخدام WHERE

حسنًا في الحياة العملية ستجد أنك تملك جداول بها مئات أو آلاف البيانات
فمثلاً جدول الطلاب قد يحتوي على مئات الطلاب، فهل كل مرة تحتاج لعمل Query لقراءة جميع البيانات ؟
الإجابة بالطبع لا، بل نحن نحتاج لبيانات محددة تحقق شروط معينة

أحيانًا نطلب أعطني جميع الطلاب الذين أعمارهم أكبر من 20 سنة
أول أعطني أعلى 10 طلاب
بمعنى أخر تريد عرض البيانات التي تحقق شروط معينة فقط

هنا يأتي دور WHERE الذي يُستخدم لكي يصفي البيانات حسب الشروط التي نحددها

شكل كتابة الأمر بسيطة جدًا فقط تضيف WHERE بعد FROM وتحدد الشروط التي تريدها

SELECT * FROM Students WHERE age > 20;

هنا قلنا له "أريد قراءة جميع البيانات من جدول الطلاب الذي أعمارهم أكبر من 20"

النتيجة ستكون:

+----+---------------+-----+-----------+------------------------+
| id | name          | age | level     | email                  |
+----+---------------+-----+-----------+------------------------+
| 3  | Mohamed Adel  | 22  | Advanced  | [email protected] |
| 4  | Kamal Mahmoud | 21  | Beginner  | [email protected]   |
+----+---------------+-----+-----------+------------------------+

بالطبع الناتج يحتوي على الطلاب الذين أعمارهم أكبر من 20 فقط
وهما شخصين في جدول الـ Students الخاص بنا وهما Mohamed Adel و Kamal Mahmoud


بالتالي يمكننا استخدام شرط WHERE لتصفية النتائج حسب ما نريد
فمثلا إذا أردنا جميع الطلاب الذين مستواهم Advanced:

SELECT * FROM Students WHERE level = 'Advanced';

النتيجة ستكون:

+----+-----------------+-----+--------------+------------------------+
| id | name            | age | level        | email                  |
+----+-----------------+-----+--------------+------------------------+
| 3  | Mohamed Adel    | 22  | Advanced     | [email protected] |
| 5  | Ayman Hassan    | 20  | Advanced     | [email protected]   |
+----+-----------------+-----+--------------+------------------------+

كما ترى أمر الـ SELECT مع شرط WHERE يسمح لنا بتصفية البيانات حسب الشروط التي نحددها بكل بساطة كأنك تتحدث مع قاعدة البيانات

ربط شروط متعددة باستخدام AND و OR

ما رأيك بإحضار الطلاب الذين مستواهم Advanced و أكبر من 20 سنة ؟
هكذا لدينا شريطين وهما مستوى الطالب و عمر الطالب
يمكنك دمج الشرطين معًا باستخدام AND أو OR حسب ما تريد

هنا بما أننا نريد الطلاب الذين مستواهم Advanced و أعمارهم أكبر من 20
فسنستخدم AND لربط الشرطين معًا:

SELECT * FROM Students WHERE level = 'Advanced' AND age > 20;

لاحظ أننا استخدمنا AND لربط شرطين معًا
مثل اللغة الإنجليزية، يمكننا استخدام AND لربط أكثر من شرط معًا وبالتالي يجب أن تتحقق جميع الشروط لكي تكون النتيجة صحيحة أو نستحدم OR لربط أكثر من شرط بحيث يكفي أن يتحقق شرط واحد فقط وهذه الشروط مرتبطة بنوع من البيانات قد تعرفه أو درسته في عالم البرمجة وهو الـ Boolean
وهو تمامًا ما نستخدمه في الـ SQL في الـ WHERE

والـ Boolean هو نوع من البيانات مثل Integer أو String يمكن أن يكون له قيمتين فقط true أو false
عندما نستخدم AND، يجب أن تكون جميع الشروط صحيحة true لكي يتحقق الشرط
وعندما نستخدم OR، يكفي أن يكون أحد الشروط صحيحًا true لكي تتحقق النتيجة

على أي حال نتيجة الـ Query التى كتبناها سابقًا ستكون:

+----+-----------------+-----+-----------+------------------------+
| id | name            | age | level     | email                  |
+----+-----------------+-----+-----------+------------------------+
| 3  | Mohamed Adel    | 22  | Advanced  | [email protected] |
+----+-----------------+-----+-----------+------------------------+

هنا لدينا طالب واحد فقط وهو Mohamed Adel وهو الذي حقق الشرطين level = 'Advanced' و age > 20
إذا أردنا الطلاب الذين مستواهم Advanced أو أعمارهم أكبر من 20، يمكننا استخدام OR:

SELECT * FROM Students WHERE level = 'Advanced' OR age > 20;

النتيجة ستكون:

+----+-----------------+-----+-----------+------------------------+
| id | name            | age | level     | email                  |
+----+-----------------+-----+-----------+------------------------+
| 3  | Mohamed Adel    | 22  | Advanced  | [email protected] |
| 4  | Kamal Mahmoud   | 21  | Beginner  | [email protected]   |
| 5  | Ayman Hassan    | 20  | Advanced  | [email protected]   |
+----+-----------------+-----+-----------+------------------------+

وبالطبع يمكنك دمج الشروط باستخدام AND و OR بالعدد الذي تريده

أهمية استخدام الأقواس

الأقواس () في الـ SQL مهمة جدًا لتحديد أولوية تنفيذ الشروط، تمامًا مثل الرياضيات
دعنا نرى مثالاً عملياً:

المثال الأول: مع استخدام الأقواس

SELECT * FROM Students
WHERE (level = 'Advanced' OR level = 'Beginner')
AND age > 20;

هنا نقول: "أريد الطلاب الذين (مستواهم Advanced أو Beginner) وفي نفس الوقت عمرهم أكبر من 20"

بسبب الأقواس، سيتم تنفيذ الشرط بهذا الترتيب:

  1. أولًا: (level = 'Advanced' OR level = 'Beginner') - نجد الطلاب المتقدمين أو المبتدئين
  2. ثانيًا: AND age > 20 - نأخذ الطلاب الذي عمرهم أكبر من 20

وهنا بسبب وجود AND فسيجب أن يتحقق الشرطين معًا، الشرط الذي على يسار الـ AND والشرط الذي على يمينه

النتيجة:

+----+-----------------+-----+--------------+------------------------+
| id | name            | age | level        | email                  |
+----+-----------------+-----+--------------+------------------------+
| 3  | Mohamed Adel    | 22  | Advanced     | [email protected] |
| 4  | Kamal Mahmoud   | 21  | Beginner     | [email protected]   |
+----+-----------------+-----+--------------+------------------------+

المثال الثاني: بدون استخدام الأقواس

SELECT * FROM Students
WHERE level = 'Advanced' OR level = 'Beginner' AND age > 20;

هنا بدون أقواس، الـ SQL يطبق قاعدة الأولوية في التنفيذ بالتالي الـ AND له أولوية أعلى من الـ OR

كأنك كتبت الشرط هكذا:

SELECT * FROM Students
WHERE level = 'Advanced' OR (level = 'Beginner' AND age > 20);

لذلك سيتم تنفيذ الشرط كالتالي:

  1. أولًا: level = 'Advanced' - نجد الطلاب المتقدمين
  2. ثانيًا: (level = 'Beginner' AND age > 20) - نجد الطلاب المبتدئين الذين عمرهم أكبر من 20

وهنا بسبب وجود OR بين الشرطين، يكفي أن يتحقق أحد الشرطين لكي تظهر النتيجة

النتيجة ستكون كالتالي:

+----+-----------------+-----+--------------+------------------------+
| id | name            | age | level        | email                  |
+----+-----------------+-----+--------------+------------------------+
| 3  | Mohamed Adel    | 22  | Advanced     | [email protected] |
| 4  | Kamal Mahmoud   | 21  | Beginner     | [email protected]   |
| 5  | Ayman Hassan    | 20  | Advanced     | [email protected]   |
+----+-----------------+-----+--------------+------------------------+

لاحظ الفرق:

  • في المثال الأول بالشرط (level = 'Advanced' OR level = 'Beginner') AND age > 20
    ظهر طالبين فقط وهم
    • Mohamed Adel مستواه Advanced وعمره أكبر من 20
    • Kamal Mahmoud مستواه Beginner وعمره أكبر من 20
    • هنا نحن نطبق الشرطين معًا بسبب وجود AND
  • في المثال الثاني بالشرط level = 'Advanced' OR (level = 'Beginner' AND age > 20)
    ظهر ثلاثة طلاب وهم
    • Mohamed Adel مستواه Advanced
    • Kamal Mahmoud مستواه Beginner وعمره أكبر من 20
    • Ayman Hassan مستواه Advanced
    • هنا يكفي أن نطبق أحد الشرطين بسبب وجود OR

عوامل المقارنة المختلفة في WHERE

في الـ SQL، نستخدم عوامل المقارنة مثل = و > و < وغيرها لتحديد الشروط التي نريدها في جملة WHERE
لدينا أنواع كثيرة منها الأساسية التي سنتناولها في هذا القسم
ثم لدينا أنواع مميزة أو متقدمة مثل LIKE و IN و BETWEEN وغيرها

العوامل الأساسية

  • =: يساوي
  • != أو <>: لا يساوي
  • >: أكبر من
  • <: أصغر من
  • >=: أكبر من أو يساوي
  • <=: أصغر من أو يساوي

لا أظن أن عليا شرح هذه العوامل لأنها واضحة جدًا لذا لا داعي للاستفاضة فيها، لكن دعنا نرى بعض الأمثلة العملية على استخدامها بشكل سريع

-- الطلاب الذين مستواهم متقدم
SELECT name, level FROM Students WHERE level = 'Advanced';

-- الطلاب الذين ليسوا مبتدئين
SELECT name, level FROM Students WHERE level != 'Beginner';

-- الطلاب الذين عمرهم أكبر من 20
SELECT name, age FROM Students WHERE age > 20;

-- الطلاب الذين عمرهم أقل من أو يساوي 20
SELECT name, age FROM Students WHERE age <= 20;

ملحوظة: في بعض أنظمة الـ DBMS، يمكنك استخدام <> بدلاً من != أو العكس أو كليهما بحسب الـ DBMS الذي تستخدمه

البحث عن جملة باستخدام LIKE

أحيانًا نحتاج للبحث عن البيانات التي تحتوي على جزء معين من النص وليس النص بالكامل
مثلاً، إذا كنت تريد البحث عن جميع الطلاب الذين أسماؤهم تبدأ بحرف معين أو تحتوي على كلمة معينة

هنا يأتي دور LIKE الذي يُستخدم للبحث في النصوص باستخدام مثل هذه الشروط

رموز البحث المستخدمة مع LIKE

عندما نستخدم LIKE، لدينا رمزين مهمين للبحث:

  • %: يمثل أي عدد من الأحرف
  • _: يمثل حرف واحد فقط

هذه العلامات تسمى Wildcards ويستخدمان في الـ LIKE لتحدد شكل الجملة التي تريدها

أمثلة على استخدام LIKE مع %

دعنا نرى أمثلة عملية لاستخدام % مع LIKE:

البحث عن الطلاب الذين أسماؤهم تبدأ بـ "A":

SELECT * FROM Students WHERE name LIKE 'A%';

هنا A% يعني أي اسم يبدأ بـ "A" ويتبعه أي شيء
بحيث أن العلامة % تمثل أي عدد من الأحرف
كأنك تقول A(أي شيء)

النتيجة:

+----+----------------+-----+-----------+----------------------+
| id | name           | age | level     | email                |
+----+----------------+-----+-----------+----------------------+
| 1  | Ahmed Moustafa | 20  | Beginner  | [email protected] |
| 5  | Ayman Hassan   | 20  | Advanced  | [email protected] |
+----+----------------+-----+-----------+----------------------+

هنا لدينا فقط الطلاب الذين أسماؤهم تبدأ بحرف A وهما Ahmed Moustafa و Ayman Hassan
بالتالي يمكنك تخيل أن A% كان مجرد قالب يبحث عن أي اسم يبدأ بـ A ويتبعه أي شيء
وهنا الاسم Ahmed Moustafa يحقق الشرط لأنه يبدأ بـ A والجزء المتبقي من الاسم هو hmed Moustafa الذي تمثله العلامة %
والاسم Ayman Hassan أيضًا يحقق الشرط لأنه يبدأ بـ A والجزء المتبقي هو yman Hassan الذي تمثله العلامة %


البحث عن الطلاب الذين أسماؤهم تنتهي بـ "Ali":

SELECT * FROM Students WHERE name LIKE '%Ali';

هنا %Ali يعني أي اسم ينتهي بـ "Ali" ويسبقه أي شيء
لأننا وضعنا % قبل Ali، أي عدد من الحروف يتبعها كلها
كأنك تقول (أي شيء)Ali

النتيجة:

+----+-----------+-----+--------------+----------------------+
| id | name      | age | level        | email                |
+----+-----------+-----+--------------+----------------------+
| 2  | Osama Ali | 19  | Intermediate | [email protected] |
+----+-----------+-----+--------------+----------------------+

هنا حصنا على طالب واحد فقط وهو Osama Ali الذي اسمه ينتهي بـ Ali
وهنا نفس الفكرة فالشكل الذي اخترناه %Ali
بالتالي الجزء الذي كان تمثله العلامة % هو Osama ثم يتبعها Ali


البحث عن الطلاب الذين أسماؤهم تحتوي على "med":

SELECT * FROM Students WHERE name LIKE '%med%';

هنا استخدمنا % قبل وبعد med وهذا يعني أي اسم يحتوي على "med" في أي مكان مهما كان موقعها
كأنك تقول (أي شيء)med(أي شيء)

النتيجة:

+----+--------------+-----+-----------+------------------------+
| id | name         | age | level     | email                  |
+----+--------------+-----+-----------+------------------------+
| 3  | Mohamed Adel | 22  | Advanced  | [email protected] |
+----+--------------+-----+-----------+------------------------+

هنا لدينا طالب واحد فقط وهو Mohamed Adel الذي يحتوي اسمه على med في منتصف الاسم
وكالامثلة السابقة فـ %med% هو شكل قالب يبحث عن أي اسم يحتوي على med في أي مكان
بالتالي الجزء الذي تمثله العلامة % الأولى هو Moha ثم يتبعها الجزء med ثم يتبعها الجزء الذي تمثله العلامة % الثانية وهو Adel

أمثلة على استخدام LIKE مع _

الآن دعنا نرى أمثلة على استخدام _ للبحث عن حرف واحد فقط
على عكس % التي كانت تمثل أي عدد من الأحرف، فالعلامة _ تمثل حرف واحد فقط

البحث عن الأسماء التي تبدأ بـ "A" ويليها حرف واحد ثم "med":

SELECT * FROM Students WHERE name LIKE 'A_med%';

هنا A_med% يعني أي اسم يبدأ بـ "A" ثم حرف واحد فقط ثم "med" ثم أي شيء
العلامة _ تمثل حرف واحد بالضبط، بينما % تمثل أي عدد من الأحرف
يمكنك قراءة A_med% كالتالي: A(حرف واحد)med(أي شيء)

بالتالي النتيجة ستكون:

+----+----------------+-----+-----------+----------------------+
| id | name           | age | level     | email                |
+----+----------------+-----+-----------+----------------------+
| 1  | Ahmed Moustafa | 20  | Beginner  | [email protected] |
+----+----------------+-----+-----------+----------------------+

البحث عن الطلاب الذين أسماؤهم الأولى مكونة من 5 أحرف بالضبط:

SELECT * FROM Students WHERE name LIKE '_____ %';

هنا استخدمنا 5 من العلامة _ ثم مسافة ثم %
وهذا يعني أي اسم مكون من 5 ثم مسافة ثم أي شيء
كأنك تقول 5 أحرف بالضبط + مسافة + (أي شيء)

النتيجة ستكون:

+----+-----------------+-----+--------------+------------------------+
| id | name            | age | level        | email                  |
+----+-----------------+-----+--------------+------------------------+
| 1  | Ahmed Moustafa  | 20  | Beginner     | [email protected]   |
| 2  | Osama Ali       | 19  | Intermediate | [email protected]   |
| 4  | Kamal Mahmoud   | 21  | Beginner     | [email protected]   |
| 5  | Ayman Hassan    | 20  | Advanced     | [email protected]   |
+----+-----------------+-----+--------------+------------------------+

هنا لدينا جميع الطلاب الذين أسماؤهم الأولى مكونة من 5 أحرف بالضبط
وهذا لأننا استخدمنا _____ لتمثيل 5 أحرف بالضبط، ثم مسافة، ثم % لتمثيل أي شيء بعد ذلك
ولاحظ أن الطالب Mohamed Adel لم يظهر لأنه اسمه مكون من 6 أحرف وليس 5

البحث في مجموعة من القيم باستخدام IN

في بعض الأحيان نريد البحث عن البيانات التي تتطابق مع مجموعة من القيم
فمثلًا نريد جميع الطلاب الذين مستواهم Advanced أو Intermediate هنا قد تقول لي يمكننا استخدام OR للقيام بذلك

SELECT * FROM Students WHERE level = 'Advanced' OR level = 'Intermediate';

وهذا كود سليم وبلا أي مشكلة، لكن هناك طريقة أفضل وأكثر كفاءة وهي استخدام IN للبحث في مجموعة من القيم
فالـ IN يسمح لك بتحديد مجموعة من القيم في شرط واحد

يمكننا استخدام IN لتبسيط الـ Query السابقة:

SELECT * FROM Students WHERE level IN ('Advanced', 'Intermediate');

هذا الأمر يعني "أريد الطلاب الذين مستواهم ضمن هذه القائمة"

النتيجة:

+----+-----------------+-----+--------------+------------------------+
| id | name            | age | level        | email                  |
+----+-----------------+-----+--------------+------------------------+
| 2  | Osama Ali       | 19  | Intermediate | [email protected]   |
| 3  | Mohamed Adel    | 22  | Advanced     | [email protected] |
| 5  | Ayman Hassan    | 20  | Advanced     | [email protected]   |
+----+-----------------+-----+--------------+------------------------+

هذا سيعطينا جميع الطلاب الذين مستواهم Advanced أو Intermediate


أريد جميع الطلاب الذين أعمارهم 20 أو 22 سنة

SELECT * FROM Students WHERE age IN (20, 22);

هذا سيعطينا جميع الطلاب الذين أعمارهم 20 أو 22 سنة

النتيجة ستكون:

+----+-----------------+-----+--------------+------------------------+
| id | name            | age | level        | email                  |
+----+-----------------+-----+--------------+------------------------+
| 1  | Ahmed Moustafa  | 20  | Beginner     | [email protected]   |
| 3  | Mohamed Adel    | 22  | Advanced     | [email protected] |
| 5  | Ayman Hassan    | 20  | Advanced     | [email protected]   |
+----+-----------------+-----+--------------+------------------------+

استبعاد مجموعة من القيم باستخدام NOT IN

إذا كنت تريد عكس الـ IN، أي الطلاب الذين لا يحققون هذه القيم، يمكنك استخدام NOT IN
يمكنك البحث عن الطلاب الذين ليس مستواهم Advanced أو Intermediate
بدلاً من استخدام AND وكتابة الشرط هكذا level != 'Advanced' AND level != 'Intermediate'

يمكنك استخدام NOT IN لتبسيط الأمر:

SELECT * FROM Students WHERE level NOT IN ('Advanced', 'Intermediate');

هذا سيعطينا جميع الطلاب الذين مستواهم ليس Advanced أو Intermediate

النتيجة:

+----+----------------+-----+-----------+----------------------+
| id | name           | age | level     | email                |
+----+----------------+-----+-----------+----------------------+
| 1  | Ahmed Moustafa | 20  | Beginner  | [email protected] |
| 4  | Kamal Mahmoud  | 21  | Beginner  | [email protected] |
+----+----------------+-----+-----------+----------------------+

البحث في نطاق معين باستخدام BETWEEN

عندما تريد البحث عن البيانات ضمن نطاق معين، مثل الطلاب الذين أعمارهم بين 19 و 21 سنة
بدلًا من استخدام AND وكتابة الشرط هكذا age >= 19 AND age <= 21
يمكنك استخدام BETWEEN لتبسيط الأمر

فبدلاً من كتابة هذه الـ Query:

SELECT * FROM Students WHERE age >= 19 AND age <= 21;

يمكنك كتابتها بهذه الطريقة:

SELECT * FROM Students WHERE age BETWEEN 19 AND 21;

هذا الأمر يعني "أريد الطلاب الذين أعمارهم بين 19 و 21 سنة"

النتيجة ستكون:

+----+-----------------+-----+--------------+------------------------+
| id | name            | age | level        | email                  |
+----+-----------------+-----+--------------+------------------------+
| 1  | Ahmed Moustafa  | 20  | Beginner     | [email protected]   |
| 2  | Osama Ali       | 19  | Intermediate | [email protected]   |
| 4  | Kamal Mahmoud   | 21  | Beginner     | [email protected]   |
| 5  | Ayman Hassan    | 20  | Advanced     | [email protected]   |
+----+-----------------+-----+--------------+------------------------+

استبعاد القيم من نطاق معين باستخدام NOT BETWEEN

إذا كنت تريد استبعاد مجموعة من القيم من نطاق معين، فيمكنك استخدام NOT BETWEEN
على سبيل المثال، إذا كنت تريد جميع الطلاب الذين أعمارهم ليست بين 19 و 21 سنة
بدلاً من كتابة الشرط هكذا age < 19 OR age > 21

يمكنك استخدام NOT BETWEEN:

SELECT * FROM Students WHERE age NOT BETWEEN 19 AND 21;

هذا سيعطينا الطلاب الذين أعمارهم ليست بين 19 و 21 سنة

النتيجة:

+----+--------------+-----+-----------+------------------------+
| id | name         | age | level     | email                  |
+----+--------------+-----+-----------+------------------------+
| 3  | Mohamed Adel | 22  | Advanced  | [email protected] |
+----+--------------+-----+-----------+------------------------+

التعامل مع القيم الفارغة NULL

في قواعد البيانات، أحيانًا تكون بعض البيانات فارغة أو غير محددة
هذه القيم تسمى NULL وهي تختلف عن النص الفارغ '' أو الرقم صفر 0
فالـ NULL يعني "لا توجد قيمة" أو "القيمة غير معروفة"

حاليا لا يوجد لدينا أي عمود في جدولنا يحتوي على NULL، لكن دعنا نضيف بعض البيانات التي تحتوي على NULL

INSERT INTO Students (name, email, age, level) VALUES
('Adam Ibrahim', '[email protected]', NULL, 'Beginner'),
('Ismail Khaled', '[email protected]', 22, NULL),
('Ali Hassan', '[email protected]', NULL, NULL);

الآن لدينا طلاب لديهم أعمار غير محددة أو مستويات غير محددة

+----+-----------------+------+--------------+------------------------+
| id | name            | age  | level        | email                  |
+----+-----------------+------+--------------+------------------------+
| 1  | Ahmed Moustafa  | 20   | Beginner     | [email protected]   |
| 2  | Osama Ali       | 19   | Intermediate | [email protected]   |
| 3  | Mohamed Adel    | 22   | Advanced     | [email protected] |
| 4  | Kamal Mahmoud   | 21   | Beginner     | [email protected]   |
| 5  | Ayman Hassan    | 20   | Advanced     | [email protected]   |
| 6  | Adam Ibrahim    | NULL | Beginner     | [email protected]    |
| 7  | Ismail Khaled   | 22   | NULL         | [email protected]  |
| 8  | Ali Hassan      | NULL | NULL         | [email protected]     |
+----+-----------------+------+--------------+------------------------+

البحث عن القيم الفارغة باستخدام IS NULL

للبحث عن القيم الفارغة، لا يمكنك استخدام = أو !=
لأن NULL لا يمكن مقارنته بهذه الطريقة فهو ليس قيمة فعلية
لذلك، يجب استخدام IS NULL:

SELECT * FROM Students WHERE age IS NULL;

هذا سيعطينا جميع الطلاب الذين عمرهم غير محدد

بالتالي النتيجة ستكون:

+----+--------------+------+-----------+------------------------+
| id | name         | age  | level     | email                  |
+----+--------------+------+-----------+------------------------+
| 6  | Adam Ibrahim | NULL | Beginner  | [email protected]    |
| 8  | Ali Hassan   | NULL | NULL      | [email protected]     |
+----+--------------+------+-----------+------------------------+

ملحوظة: لا تستخدم = أو != أو <> مع NULL لأنها دائمًا ستعيد false
لأن NULL لا يساوي أي شيء، حتى نفسه بالتالي لو قمت بعمل NULL = NULL ستجدها تعيد false
لذلك استخدم IS NULL أو IS NOT NULL عندما تتعامل مع NULL

من أجل فقط اشباع الفضول، إذا كتبت age = NULL بدلاً من age IS NULL

النتيجة ستكون:

+----+-----------------+------+--------------+------------------------+
| id | name            | age  | level        | email                  |
+----+-----------------+------+--------------+------------------------+

لا يوجد شيء برغم من أن هناك طلاب أعمارهم NULL
لكن بسبب أننا استخدمنا = مع NULL، فستكون النتيجة دائمًا false
لذا استخدم IS NULL بدلاً من = عند التعامل مع NULL

البحث عن القيم غير الفارغة باستخدام IS NOT NULL

بالطبع يمكنك أيضًا البحث عن القيم التي ليست NULL باستخدام IS NOT NULL

SELECT * FROM Students WHERE age IS NOT NULL;

هذا سيعطينا جميع الطلاب الذين عمرهم محدد ولها قيمة

النتيجة ستكون:

+----+-----------------+------+--------------+------------------------+
| id | name            | age  | level        | email                  |
+----+-----------------+------+--------------+------------------------+
| 1  | Ahmed Moustafa  | 20   | Beginner     | [email protected]   |
| 2  | Osama Ali       | 19   | Intermediate | [email protected]   |
| 3  | Mohamed Adel    | 22   | Advanced     | [email protected] |
| 4  | Kamal Mahmoud   | 21   | Beginner     | [email protected]   |
| 5  | Ayman Hassan    | 20   | Advanced     | [email protected]   |
| 7  | Ismail Khaled   | 22   | NULL         | [email protected]  |
+----+-----------------+------+--------------+------------------------+

كل شيء قلناه عن IS NULL ينطبق أيضًا على IS NOT NULL خصوصًا في التعامل مع NULL بالـ = أو != أو <>

ترتيب النتائج باستخدام ORDER BY

عندما تقوم بعمل أي Query على البيانات، قد تحتاج إلى ترتيبها بطريقة معينة
مثلاً، تريد عرض الطلاب مرتبين حسب العمر من الأصغر للأكبر، أو حسب الاسم أبجديًا

هنا يأتي دور أمر الـ ORDER BY الذي يستخدم لترتيب النتائج سواءً بشكل تصاعدي أو تنازلي حسب العمود الذي تحدده

الترتيب التصاعدي

وهو الترتيب من الأصغر للأكبر أو من الأبجدية A إلى Z
وهو الترتيب الافتراضي في الـ SQL
بالتالي عندما تستخدم ORDER BY بدون تحديد نوع الترتيب، سيتم الترتيب تصاعديًا بشكل افتراضي

لنفترض أننا نريد عرض جميع الطلاب مرتبين حسب العمر من الأصغر للأكبر
نقوم فقط بكتابة ORDER BY متبوعًا باسم العمود الذي نريد الترتيب حسبه، وهو age في هذه الحالة:

SELECT * FROM Students ORDER BY age;

هذا الأمر سيعرض الطلاب مرتبين حسب العمر من الأصغر للأكبر

النتيجة:

+----+-----------------+-----+--------------+------------------------+
| id | name            | age | level        | email                  |
+----+-----------------+-----+--------------+------------------------+
| 6  | Adam Ibrahim    | NULL| Beginner     | [email protected]    |
| 8  | Ali Hassan      | NULL| NULL         | [email protected]     |
| 2  | Osama Ali       | 19  | Intermediate | [email protected]   |
| 1  | Ahmed Moustafa  | 20  | Beginner     | [email protected]   |
| 5  | Ayman Hassan    | 20  | Advanced     | [email protected]   |
| 4  | Kamal Mahmoud   | 21  | Beginner     | [email protected]   |
| 3  | Mohamed Adel    | 22  | Advanced     | [email protected] |
| 7  | Ismail Khaled   | 22  | NULL         | [email protected]  |
+----+-----------------+-----+--------------+------------------------+

لاحظ أن الطلاب الذين ليس لديهم أعمار أي قيمها NULL تظهر أولاً في الترتيب
لأنها تعتبر أقل من أي قيمة عددية أخرى في الترتيب
إذا كنت تريد تجاهل القيم NULL في الترتيب، يمكنك استخدام WHERE لتصفية النتائج قبل الترتيب:

SELECT * FROM Students WHERE age IS NOT NULL
ORDER BY age;

فقط نستخدم WHERE age IS NOT NULL لتجاهل الطلاب الذين أعمارهم NULL

النتيجة ستكون:

+----+-----------------+-----+--------------+------------------------+
| id | name            | age | level        | email                  |
+----+-----------------+-----+--------------+------------------------+
| 2  | Osama Ali       | 19  | Intermediate | [email protected]   |
| 1  | Ahmed Moustafa  | 20  | Beginner     | [email protected]   |
| 5  | Ayman Hassan    | 20  | Advanced     | [email protected]   |
| 4  | Kamal Mahmoud   | 21  | Beginner     | [email protected]   |
| 3  | Mohamed Adel    | 22  | Advanced     | [email protected] |
| 7  | Ismail Khaled   | 22  | NULL         | [email protected]  |
+----+-----------------+-----+--------------+------------------------+

هنا تم تجاهل الطلاب الذين أعمارهم NULL وتم ترتيب البقية حسب العمر من الأصغر للأكبر

لو أردت أن توضح أو تأكد أن الترتيب تصاعدي، يمكنك استخدام ASC:

SELECT * FROM Students WHERE age IS NOT NULL
ORDER BY age ASC;

كلمة ASC تعني Ascending أي تصاعدي، وهي تخبر الـ SQL أن يرتب النتائج من الأصغر للأكبر
لكن بما أن الترتيب التصاعدي هو الافتراضي، يمكنك تجاهل ASC واستخدام ORDER BY age فقط دون الحاجة لتحديد ASC

الترتيب التنازلي

للترتيب من الأكبر للأصغر، استخدم DESC بعد اسم العمود في ORDER BY لتحديد الترتيب التنازلي:

SELECT * FROM Students WHERE age IS NOT NULL
ORDER BY age DESC;

لاحظ أن الـ Query هنا نفسها السابقة، لكننا أضفنا DESC بعد age بدلاً من ASC لإخبار الـ SQL أن يرتب النتائج من الأكبر للأصغر
ولاحظ أن DESC تعني Descending أي تنازلي، وبالتالي هذا سيعرض الطلاب مرتبين حسب العمر من الأكبر للأصغر

النتيجة ستكون:

+----+-----------------+-----+--------------+------------------------+
| id | name            | age | level        | email                  |
+----+-----------------+-----+--------------+------------------------+
| 3  | Mohamed Adel    | 22  | Advanced     | [email protected] |
| 7  | Ismail Khaled   | 22  | NULL         | [email protected]  |
| 4  | Kamal Mahmoud   | 21  | Beginner     | [email protected]   |
| 1  | Ahmed Moustafa  | 20  | Beginner     | [email protected]   |
| 5  | Ayman Hassan    | 20  | Advanced     | [email protected]   |
| 2  | Osama Ali       | 19  | Intermediate | [email protected]   |
+----+-----------------+-----+--------------+------------------------+

الترتيب بأكثر من عمود

حسنًا تخيل معين أنك ترتب الطلاب حسب أعمارهم، سيظهر لك طلاب لديهم نفس العمر
مثلاً، Ahmed Moustafa و Ayman Hassan كلاهما عمره 20 سنة
هنا تسأل نفسك هل إذا تساوى العمر تريد أن تعطي الأولية للترتيب حسب الاسم ؟
مثلًا Ahmed يأتي قبل Ayman أم العكس Ayman يأتي قبل Ahmed ؟

هنا يمكنك استخدام ORDER BY مع أكثر من عمود لترتيب النتائج بشكل ثانوي في حالة تساوي العمود الأول هذا مفيد جداً عندما يكون لديك قيم متشابهة في العمود الأول وتريد ترتيب ثانوي للقيم المتشابهة

SELECT * FROM Students WHERE age IS NOT NULL
ORDER BY age DESC;

هنا نفس الـ Query السابقة، فقط نرتب الطلاب حسب العمر تنازلياً
لكي اذكرك بشكل النتيجة عندما نرتب حسب العمر تنازلياً

+----+-----------------+-----+--------------+------------------------+
| id | name            | age | level        | email                  |
+----+-----------------+-----+--------------+------------------------+
| 3  | Mohamed Adel    | 22  | Advanced     | [email protected] |
| 7  | Ismail Khaled   | 22  | NULL         | [email protected]  |
| 4  | Kamal Mahmoud   | 21  | Beginner     | [email protected]   |
| 1  | Ahmed Moustafa  | 20  | Beginner     | [email protected]   |
| 5  | Ayman Hassan    | 20  | Advanced     | [email protected]   |
| 2  | Osama Ali       | 19  | Intermediate | [email protected]   |
+----+-----------------+-----+--------------+------------------------+

الآن ستلاحظ أن لدينا طلاب لديهم نفس العمر، مثل Mohamed Adel و Ismail Khaled كلاهما عمره 22 سنة
و Ahmed Moustafa و Ayman Hassan كلاهما عمره 20 سنة

هنا نريد أن نرتبهم حسب الإسم في حالة تساوي العمر
بالتالي نضيف عمود name في جملة ORDER BY:

SELECT * FROM Students
WHERE age IS NOT NULL
ORDER BY age DESC, name ASC;

هنا نحن نرتب النتائج حسب age تنازلياً، ثم إذا تساوى العمر نرتب حسب name تصاعدياً
لاحظ أن كل عمود في ORDER BY يمكن أن يكون له ترتيب مستقل، سواء تصاعدي ASC أو تنازلي DESC
فهنا كتبنا age DESC ثم name ASC وفصلنا بينهما بـ ,

على أي حال النتيجة ستكون:

+----+-----------------+-----+--------------+------------------------+
| id | name            | age | level        | email                  |
+----+-----------------+-----+--------------+------------------------+
| 7  | Ismail Khaled   | 22  | NULL         | [email protected]  |
| 3  | Mohamed Adel    | 22  | Advanced     | [email protected] |
| 4  | Kamal Mahmoud   | 21  | Beginner     | [email protected]   |
| 1  | Ahmed Moustafa  | 20  | Beginner     | [email protected]   |
| 5  | Ayman Hassan    | 20  | Advanced     | [email protected]   |
| 2  | Osama Ali       | 19  | Intermediate | [email protected]   |
+----+-----------------+-----+--------------+------------------------+

لاحظ أن Ismail Khaled يأتي أولاً لأنه اسمه يبدأ بحرف I، بينما Mohamed Adel يأتي بعده لأنه اسمه يبدأ بحرف M
وقيمة الحرف I في الـ ASCII تساوي 73 بينما قيمة الحرف M تساوي 77
بالتالي لأننا رتبنا حسب name تصاعدياً من الأصغر للأكبر، فـ I أصغر من M فأتى Ismail Khaled أولاً

لو أردنا أن نرتب حسب name تنازلياً، يمكننا تغيير ASC إلى DESC:

SELECT * FROM Students
WHERE age IS NOT NULL
ORDER BY age DESC, name DESC;

النتيجة ستكون:

+----+-----------------+-----+--------------+------------------------+
| id | name            | age | level        | email                  |
+----+-----------------+-----+--------------+------------------------+
| 3  | Mohamed Adel    | 22  | Advanced     | [email protected] |
| 7  | Ismail Khaled   | 22  | NULL         | [email protected]  |
| 4  | Kamal Mahmoud   | 21  | Beginner     | [email protected]   |
| 5  | Ayman Hassan    | 20  | Advanced     | [email protected]   |
| 1  | Ahmed Moustafa  | 20  | Beginner     | [email protected]   |
| 2  | Osama Ali       | 19  | Intermediate | [email protected]   |
+----+-----------------+-----+--------------+------------------------+

لاحظ أن Mohamed Adel أتى أولاً لأنه اسمه يبدأ بحرف M، بينما Ismail Khaled يأتي بعده لأنه اسمه يبدأ بحرف I
لأننا رتبنا حسب name تنازلياً، فـ M أكبر من I، فأتى Mohamed Adel أولاً
وأيضًا Ayman Hassan أتى أولًَا قبل Ahmed Moustafa لأن قيمة y تساوي 121 في الـ ASCII وهي أكبر من قيمة h التي تساوي 104

ملحوظة: جدول الـ ASCII هو جدول به 128 حرفًا ورمزًا وكل واحد منها يقابله عدد معين
فمثلًا الحرف A يقابله الرقم 65 والحرف B يقابله الرقم 66 والحرف C يقابله الرقم 67 وهكذا
يمكنك الاطلاع على جدول الـ ASCII من هنا

تحديد عدد النتائج باستخدام LIMIT

في التطبيقات الحقيقية، قد تحتوي الجداول على آلاف أو حتى ملايين البيانات
وحتى لو استخدمت WHERE لتصفية النتائج، قد تظل النتائج كبيرة جدًا
تخيل أن جدول الطلاب يحتوي على 50000 طالب، وأنت تريد عرض أول 5 طلاب فقط ماذا ستفعل ؟ أول مثلًا قمت بترتيبهم بحسب أعمارهم وتريد عرض أكبر طالب سناً

هنا يأتي دور الـ LIMIT الذي يخبر الـ SQL أن تحضر عدد محدد من النتائج فقط
وهذا يوفر الوقت والذاكرة ويحسن الأداء بشكل كبير وكل هذه الكلمات الرنانة

SELECT * FROM Students LIMIT 3;

هذا الأمر ببساطة يعني "أحضر لي فقط أول 3 طلاب من الجدول"

النتيجة:

+----+-----------------+-----+--------------+------------------------+
| id | name            | age | level        | email                  |
+----+-----------------+-----+--------------+------------------------+
| 1  | Ahmed Moustafa  | 20  | Beginner     | [email protected]   |
| 2  | Osama Ali       | 19  | Intermediate | [email protected]   |
| 3  | Mohamed Adel    | 22  | Advanced     | [email protected] |
+----+-----------------+-----+--------------+------------------------+

ملحوظة: الـ LIMIT يحضر النتائج بالترتيب الذي تم إضافة البيانات في الجدول في حالة إذا لم تستخدم ORDER BY بالطبع

بالتالي الاستخدام الأكثر شيوعاً هو دمج LIMIT مع ORDER BY للحصول على نتائج مرتبة ثم تحديد عدد النتائج
ويمكنك دمجهم مع WHERE لتصفية النتائج قبل تطبيق LIMIT

فمثًا أحضر لي أكبر 3 طلاب سناً:

SELECT name, age FROM Students
WHERE age IS NOT NULL
ORDER BY age DESC
LIMIT 3;

النتيجة:

+-----------------+-----+
| name            | age |
+-----------------+-----+
| Mohamed Adel    | 22  |
| Ismail Khaled   | 22  |
| Kamal Mahmoud   | 21  |
+-----------------+-----+

هنا أحضرنا أكبر 3 طلاب سناً بترتيب تنازلي حسب العمر
ولاحظ أننا استخدمنا LIMIT لتحديد عدد النتائج المعروضة إلى 3 فقط لكي لا نحضر كل البيانات في الجدول

الآن مثال أخر أحضر أصغر طالب سناً في المستوى Beginner:

SELECT name, age FROM Students
WHERE age IS NOT NULL AND level = 'Beginner'
ORDER BY age ASC
LIMIT 1;

النتيجة:

+-----------------+-----+
| name            | age |
+-----------------+-----+
| Ahmed Moustafa  | 20  |
+-----------------+-----+

الخاتمة

أظن أننا سنكتفي بهذا القدر لكي لا نطيل المقالة أكثر من هذا
يكفي أنك فهمت الأساسيات المهمة في الـ SELECT وكيفية التعامل مع البيانات
وتعرفنا على أوامر كثيرة وتطبيقنا عليهم بأمثلة عملية

وبالطبع قد تكون هناك تفاصيل تعمدت عدم ذكرها لكي لا نثقل عليك في البداية
وأوامر لم نتطرق لها لكي لا نطيل الشرح
لكن لا تقلق كل شيء سيأتي في وقته، في المقالات القادمة سنغطي أو من خلال تعلمك من ممارستك


ملخص ما تعلمناه في هذه المقالة

  • قراءة البيانات بـ SELECT * لقراءة كل الأعمدة
  • قراءة أعمدة محددة بـ SELECT column1, column2
  • تصفية البيانات بـ WHERE وعوامل المقارنة (=, >, <, !=)
  • ربط الشروط بـ AND و OR واستخدام الأقواس
  • البحث عن الجمل أو النصوص بـ LIKE مع % و _
  • البحث في مجموعة من القيم بـ IN و NOT IN
  • البحث في نطاق معين من القيم بـ BETWEEN
  • التعامل مع القيم الفارغة بـ IS NULL و IS NOT NULL
  • ترتيب النتائج بـ ORDER BY ASC/DESC
  • الترتيب بعدة أعمدة لترتيب ثانوي
  • تحديد عدد النتائج بـ LIMIT

في المقالات القادمة، سنستكمل تعلمنا عن الـ SQL ونتعلم كيف نعدل القيم بـ UPDATE وكيف نحذف البيانات بـ DELETE