تعلم SQL بخمس وصفات سهلة لإتقان استعلامات قواعد البيانات
لغة الاستعلامات الهيكلية SQL (Structured Query Language) هي لغة قوية ومعبرة للتعامل مع البيانات من قواعد البيانات العلائقية. ومع ذلك، قد تبدو مخيفة للمبتدئين. “الوصفات” التي سأشاركها معكم اليوم هي أمثلة أساسية من قاعدة بيانات بسيطة، لكن الأنماط التي ستتعلمونها هنا يمكن أن تساعدكم في كتابة استعلامات دقيقة. ستجعلكم هذه الوصفات تشعرون وكأنكم طهاة بيانات ماهرون في وقت قصير.
ملاحظة حول بناء الجملة: معظم الاستعلامات أدناه مكتوبة بالأسلوب المستخدم لـ PostgreSQL من سطر أوامر psql. يمكن لمحركات SQL المختلفة استخدام أوامر مختلفة قليلاً. يجب أن تعمل معظم الاستعلامات أدناه في معظم المحركات دون تعديل، على الرغم من أن بعض المحركات أو أدوات الواجهة الرسومية قد تتطلب حذف علامات الاقتباس حول أسماء الجداول والأعمدة.
الوصفة 1: استرجاع جميع المستخدمين الذين تم إنشاؤهم ضمن نطاق تاريخ محدد
المكونات
SELECTFROMWHEREAND
الطريقة
SELECT * FROM "Users" WHERE "created_at" > '2020-01-01' AND "created_at" < '2020-02-01' ;
هذه الوصفة البسيطة هي عنصر أساسي متعدد الاستخدامات. هنا، نقوم بإرجاع المستخدمين الذين يستوفون شرطين محددين عن طريق ربط شروط WHERE بعبارة AND. يمكننا توسيع هذا بشكل أكبر باستخدام المزيد من عبارات AND. بينما المثال هنا لنطاق تاريخ معين، تتطلب معظم الاستعلامات نوعًا من الشروط لتصفية البيانات بشكل مفيد.
الوصفة 2: البحث عن جميع التعليقات لكتاب معين، بما في ذلك المستخدم الذي أضاف التعليق
المكونات
JOIN
الطريقة
SELECT "Comments"."comment", "Users"."username" FROM "Comments" JOIN "Users" ON "Comments"."userId" = "Users"."id" WHERE "Comments"."bookId" = 1 ;
يفترض هذا الاستعلام بنية الجدول التالية:

أحد الأمور التي قد تبدأ في إرباك المبتدئين في SQL هو استخدام JOINs للعثور على البيانات من الجداول المرتبطة. يوضح مخطط علاقات الكيانات (ERD) أعلاه ثلاثة جداول: Users، Books، و Comments، وعلاقاتها. يحتوي كل جدول على عمود id الذي يظهر بخط غامق في الرسم التخطيطي لإظهار أنه المفتاح الأساسي (primary key) للجدول. هذا المفتاح الأساسي هو دائمًا قيمة فريدة ويستخدم لتمييز السجلات في الجداول.
أسماء الأعمدة المائلة userId و bookId في جدول Comments هي مفاتيح أجنبية (foreign keys)، مما يعني أنها المفتاح الأساسي في جداول أخرى وتستخدم هنا للإشارة إلى تلك الجداول. توضح الموصلات في مخطط ERD أعلاه أيضًا طبيعة العلاقات بين الجداول الثلاثة. يشير الطرف ذو النقطة الواحدة على الموصل إلى "واحد" ويشير الطرف المنقسم على الموصل إلى "متعدد"، لذا فإن جدول User له علاقة "واحد إلى متعدد" (one-to-many) بجدول Comments. يمكن للمستخدم أن يكون لديه العديد من التعليقات، على سبيل المثال، ولكن التعليق يمكن أن ينتمي إلى مستخدم واحد فقط. تتمتع جداول Books و Comments بنفس العلاقة في الرسم البياني أعلاه.
يجب أن يكون استعلام SQL منطقيًا بناءً على ما نعرفه الآن. نحن نعيد فقط الأعمدة المسماة، أي عمود comment من جدول Comments واسم المستخدم username من جدول Users المرتبط (بناءً على المفتاح الأجنبي المشار إليه). في المثال أعلاه، نقيد البحث على كتاب واحد، مرة أخرى بناءً على المفتاح الأجنبي في جدول Comments.
الوصفة 3: حساب عدد التعليقات المضافة بواسطة كل مستخدم
المكونات
COUNTASGROUP BY
الطريقة
SELECT "Users"."username", COUNT("Comments"."id") AS "CommentCount" FROM "Comments" JOIN "Users" ON "Comments"."userId" = "Users"."id" GROUP BY "Users"."id" ;
يقوم هذا الاستعلام الصغير ببعض الأشياء المثيرة للاهتمام. أسهلها فهمًا هو عبارة AS. تسمح لنا هذه العبارة بإعادة تسمية الأعمدة بشكل تعسفي ومؤقت في البيانات التي يتم إرجاعها. هنا، نعيد تسمية العمود المشتق، ولكنه مفيد أيضًا عندما يكون لديك عدة أعمدة id، حيث يمكنك إعادة تسميتها إلى أشياء مثل userId أو commentId وما إلى ذلك.
عبارة COUNT هي دالة SQL تقوم، كما تتوقع، بحساب الأشياء. هنا، نحسب عدد التعليقات المرتبطة بمستخدم. كيف تعمل؟ حسنًا، GROUP BY هي المكون الأخير المهم. دعنا نتخيل باختصار استعلامًا مختلفًا قليلاً:
SELECT "Users"."username", "Comments"."comment" FROM "Comments" JOIN "Users" ON "Comments"."userId" = "Users"."id" ;
لاحظ، لا يوجد عد أو تجميع. نريد فقط كل تعليق ومن قام به. قد يبدو الإخراج شيئًا كهذا:
username |
comment |
|---|---|
jackson |
it 's good, I liked it |
jackson |
this was ok, not the best |
quincy |
excellent read, recommended |
quincy |
not worth reading |
quincy |
I haven 't read this yet |
الآن تخيل أننا أردنا حساب تعليقات Jackson و Quincy - من السهل رؤيتها بلمحة هنا، ولكن أصعب مع مجموعة بيانات أكبر كما يمكنك أن تتخيل. تخبر عبارة GROUP BY الاستعلام بشكل أساسي بمعالجة جميع سجلات jackson كمجموعة واحدة، وجميع سجلات quincy كمجموعة أخرى. ثم تقوم دالة COUNT بحساب السجلات في تلك المجموعة وإرجاع تلك القيمة:
username |
CommentCount |
|---|---|
jackson |
2 |
quincy |
3 |
الوصفة 4: البحث عن المستخدمين الذين لم يضيفوا أي تعليق
المكونات
LEFT JOINIS NULL
الطريقة
SELECT "Users"."username" FROM "Users" LEFT JOIN "Comments" ON "Users"."id" = "Comments"."userId" WHERE "Comments"."id" IS NULL ;
يمكن أن تكون عمليات الربط المختلفة (JOINs) مربكة للغاية، لذلك لن أخوض في تفاصيلها هنا. يوجد شرح ممتاز لها هنا: Visual Representations of SQL Joins، والذي يوضح أيضًا بعض الاختلافات في بناء الجملة بين نكهات SQL المختلفة.
دعنا نتخيل نسخة بديلة من هذا الاستعلام بسرعة:
SELECT "Users"."username", "Comments"."id" AS "commentId" FROM "Users" LEFT JOIN "Comments" ON "Users"."id" = "Comments"."userId" ;
لا يزال لدينا LEFT JOIN ولكننا أضفنا عمودًا وأزلنا عبارة WHERE. قد تبدو البيانات المرتجعة شيئًا كهذا:
username |
commentId |
|---|---|
jackson |
1 |
jackson |
2 |
quincy |
NULL |
abbey |
3 |
إذن، Jackson مسؤول عن التعليقين 1 و 2، و Abbey عن التعليق 3، و Quincy لم يقم بأي تعليق. الفرق بين LEFT JOIN و INNER JOIN (ما كنا نسميه JOIN حتى الآن، وهو صالح) هو أن الربط الداخلي (INNER JOIN) يعرض فقط السجلات التي توجد فيها قيم لكلا الجدولين. الربط الأيسر (LEFT JOIN)، من ناحية أخرى، يعيد كل شيء من الجدول الأول، أو الأيسر (الذي يحدد بـ FROM)، حتى لو لم يكن هناك شيء في الجدول الأيمن. لذلك، فإن الربط الداخلي سيعرض فقط سجلات Jackson و Abbey.
الآن بعد أن أصبح بإمكاننا تصور ما يرجعه LEFT JOIN، أصبح من الأسهل فهم ما يفعله جزء WHERE...IS NULL. نحن نعيد فقط هؤلاء المستخدمين الذين تكون قيمة commentId لديهم NULL، ولا نحتاج فعليًا إلى تضمين عمود القيمة NULL في الإخراج، ومن هنا جاء حذفه الأصلي.
الوصفة 5: سرد جميع التعليقات المضافة بواسطة كل مستخدم في حقل واحد، مفصولة بعلامة الأنبوب
المكونات
GROUP_CONCATأوSTRING_AGG
الطريقة (MySQL)
SELECT "Users"."username", GROUP_CONCAT("Comments"."comment" SEPARATOR ' | ') AS "comments" FROM "Users" JOIN "Comments" ON "Users"."id" = "Comments"."userId" GROUP BY "Users"."id" ;
الطريقة (PostgreSQL)
SELECT "Users"."username", STRING_AGG("Comments"."comment", ' | ') AS "comments" FROM "Users" JOIN "Comments" ON "Users"."id" = "Comments"."userId" GROUP BY "Users"."id" ;
توضح هذه الوصفة الأخيرة اختلافًا في بناء الجملة لوظيفة مماثلة في اثنين من أشهر محركات SQL. إليك مثال على الإخراج الذي قد نتوقعه:
username |
comments |
|---|---|
jackson |
it 's good, I liked it | this was ok, not the best |
quincy |
excellent read, recommended | not worth reading |
يمكننا أن نرى هنا أن التعليقات قد تم تجميعها وربطها / تجميعها معًا في حقل سجل واحد.
خاتمة: إطلاق العنان لإبداعك في استعلامات SQL
الآن بعد أن أصبح لديك بعض "وصفات" SQL لتعتمد عليها، كن مبدعًا وقدم أطباق بياناتك الخاصة! أحب أن أفكر في WHERE و JOIN و COUNT و GROUP_CONCAT على أنها "الملح، الدهن، الحمض، الحرارة" (Salt, Fat, Acid, Heat) في طهي قواعد البيانات. بمجرد أن تعرف ما تفعله بهذه العناصر الأساسية، فأنت في طريقك إلى الإتقان.
إذا كانت هذه المجموعة مفيدة، أو لديك وصفات مفضلة أخرى لمشاركتها، اترك لي تعليقًا أو تابعني على Twitter: @JacksonBates.
الخلاصة التقنية
يعرض هذا المقال ببراعة خمسة أنماط أساسية ومتقدمة لاستعلامات SQL، مصممة خصيصًا لتبسيط التعامل مع قواعد البيانات العلائقية. من التصفية الأساسية باستخدام WHERE و AND، مروراً بفهم العلاقات المعقدة بين الجداول عبر JOINs والمفاتيح الأساسية والأجنبية، وصولاً إلى تجميع البيانات وتحويلها باستخدام COUNT و GROUP BY، وحتى معالجة السيناريوهات التي تتضمن بيانات مفقودة باستخدام LEFT JOIN و IS NULL. كما يبرز المقال أهمية الدوال التجميعية المتقدمة مثل GROUP_CONCAT و STRING_AGG لتقديم البيانات بشكل أكثر كفاءة. هذه "الوصفات" ليست مجرد أمثلة، بل هي لبنات بناء أساسية لكل مطور بيانات، وتؤكد على أن إتقانها يفتح آفاقًا واسعة لتحليل البيانات واستخراج رؤى قيمة منها.