🚀 SQL المتقدمة

استعلامات معقدة، استعلامات متداخلة، Joins، دوال التجميع وطرق العرض

📚 الفصل السابع
🎯 CS 340
👩‍🎓 شوق العمران
⏱️ دليل شامل

📑 فهرس المحتويات

قيم NULL

📘 وش يعني NULL؟

NULL يمثل غياب القيمة في قاعدة البيانات. له أكثر من تفسير:

🔑 تفسيرات NULL
  1. قيمة غير معروفة: مثلاً تاريخ ميلاد شخص ما مو معروف (معلومات ناقصة).
  2. غير متوفرة / مخفية: شخص عنده رقم جوال لكن ما يبغاه يظهر.
  3. غير منطبق: LastCollegeDegree تكون NULL لشخص ما عنده شهادة جامعية.

المنطق ثلاثي القيم

⚡ TRUE و FALSE و UNKNOWN

SQL تستخدم منطق ثلاثي القيم بدل المنطق الثنائي العادي:

  • TRUE: الشرط صحيح بالتأكيد.
  • FALSE: الشرط خطأ بالتأكيد.
  • UNKNOWN: ما نقدر نحدد (لأنه يحتوي NULL).
💡 NULL في الاستعلامات

مهم: استخدم IS NULL أو IS NOT NULL - أبدًا ما تستخدم = NULL.

-- ندوّر الموظفين اللي ما عندهم مشرف SELECT Fname, Lname FROM EMPLOYEE WHERE Super_ssn IS NULL; -- ندوّر الموظفين اللي عندهم مشرف SELECT Fname, Lname FROM EMPLOYEE WHERE Super_ssn IS NOT NULL;
⚠️ قواعد NULL المهمة
  • SQL تتعامل مع كل قيمة NULL على أنها مختلفة عن أي NULL ثاني.
  • NULL = NULL تعطي UNKNOWN (مو TRUE!).
  • دايمًا استخدم IS NULL أو IS NOT NULL لمقارنة NULL.
🔄

الاستعلامات المتداخلة (Subqueries)

📘 وش هي الاستعلامات المتداخلة؟

الاستعلامات المتداخلة هي أوامر SELECT-FROM-WHERE كاملة تكون داخل استعلام SQL ثاني. تسمح لك تستخدم ناتج استعلام كجزء من استعلام آخر.

💡 مشكلة: خطوتين مقابل خطوة واحدة

المشكلة: عايز تلقى كل ورش العمل اللي يقدمها نفس مقدم ورشة "Robot Operating System".

الطريقة ١: استعلامين منفصلين

-- الخطوة ١: نجيبي اسم المقدم SELECT Presenter FROM Workshop WHERE Title = 'Robot Operating System'; -- النتيجة: Dr. Maram -- الخطوة ٢: نستخدم اسم المقدم SELECT * FROM Workshop WHERE Presenter = 'Dr. Maram';

الطريقة ٢: استعلام واحد مع Subquery

SELECT * FROM Workshop WHERE Presenter = (SELECT Presenter FROM Workshop WHERE Title = 'Robot Operating System');

عامل IN مع الاستعلامات المتداخلة

🔑 عامل IN

عامل IN يفحص إذا كانت القيمة موجودة في مجموعة القيم اللي يرجعها الاستعلام الداخلي.

💡 مثال على IN
-- ندوّر Essns للموظفين اللي يشتغلون على نفس (مشروع, ساعات) مثل الموظف '123456789' SELECT DISTINCT Essn FROM WORKS_ON WHERE (Pno, Hours) IN (SELECT Pno, Hours FROM WORKS_ON WHERE Essn = '123456789');

عوامل المقارنة: ANY و ALL

⚙️ عوامل ANY و ALL
  • = ANY (أو SOME): TRUE إذا كانت القيمة تساوي عنصر واحد على الأقل في المجموعة (مثل IN).
  • > ANY: TRUE إذا كانت القيمة أكبر من عنصر واحد على الأقل.
  • > ALL: TRUE إذا كانت القيمة أكبر من كل العناصر في المجموعة.
  • < ALL: TRUE إذا كانت القيمة أصغر من كل العناصر في المجموعة.
💡 مثال على ALL
-- ندوّر الموظفين اللي رواتبهم أكبر من كل موظفي القسم 5 SELECT Lname, Fname FROM EMPLOYEE WHERE Salary > ALL (SELECT Salary FROM EMPLOYEE WHERE Dno = 5);

مجموعات صريحة

💡 قيم مجموعة محددة
-- ندوّر الموظفين اللي يشتغلون على مشاريع 1 أو 2 أو 3 SELECT DISTINCT Essn FROM WORKS_ON WHERE Pno IN (1, 2, 3);
🔗

الاستعلامات المتداخلة المرتبطة (Correlated Queries)

📘 وش هي الاستعلامات المرتبطة؟

الاستعلامات المرتبطة هي استعلامات متداخلة الـ WHERE فيها يشير إلى خصائص من الاستعلام الخارجي. الاستعلام الداخلي يُنفَّذ مرة لكل صف من الاستعلام الخارجي.

🔑 الخصائص المهمة
  • جملة WHERE في الاستعلام الداخلي تشير إلى الاستعلام الخارجي.
  • ما نقدر ننفذه بشكل مستقل - يعتمد على الاستعلام الخارجي.
  • ينفذ مثل الـ Loop - مرة لكل صف خارجي.
💡 مثال على استعلام مرتبط
-- ندوّر الموظفين اللي عندهم تابِع بنفس الاسم والجنس SELECT E.Fname, E.Lname FROM EMPLOYEE AS E WHERE E.Ssn IN (SELECT D.Essn FROM DEPENDENT AS D WHERE E.Fname = D.Dependent_name AND E.Sex = D.Sex);

طريقة الشغل: لكل موظف E، الاستعلام الداخلي يفحص إذا كان فيه تابِع بنفس الاسم والجنس.

🔄 طريقة التقييم

فكر فيها مثل الـ Loop:

  1. لكل صف موظف في الاستعلام الخارجي.
  2. ننفذ الاستعلام الداخلي مستخدمين قيم هذا الموظف.
  3. إذا تحقق شرط الاستعلام الداخلي، نضيف الموظف للنتائج.
💡 بديل: استعلام واحد JOIN

كثير من الاستعلامات المرتبطة نقدر نكتبها كـ JOIN:

SELECT E.Fname, E.Lname FROM EMPLOYEE AS E, DEPENDENT AS D WHERE E.Ssn = D.Essn AND E.Sex = D.Sex AND E.Fname = D.Dependent_name;

EXISTS و NOT EXISTS

📘 عامل EXISTS
  • EXISTS: يرجع TRUE إذا كان ناتج الاستعلام الداخلي يحتوي على صف واحد على الأقل.
  • NOT EXISTS: يرجع TRUE إذا كان ناتج الاستعلام الداخلي فاضي.
  • UNIQUE: يرجع TRUE إذا ما في صفوف مكررة في النتيجة.
💡 مثال على EXISTS
-- نكتب استعلام التابع بـ EXISTS SELECT E.Fname, E.Lname FROM EMPLOYEE AS E WHERE EXISTS (SELECT * FROM DEPENDENT AS D WHERE E.Ssn = D.Essn AND E.Sex = D.Sex AND E.Fname = D.Dependent_name);
💡 مثال على NOT EXISTS
-- ندوّر الموظفين اللي ما عندهم توابع SELECT Fname, Lname FROM EMPLOYEE WHERE NOT EXISTS (SELECT * FROM DEPENDENT WHERE Ssn = Essn);
🎯 متى نستخدم EXISTS
  • لما نبي نفحص وجود سجلات مرتبطة.
  • أكفأ من IN مع البيانات الكبيرة.
  • يشتغل ممتاز مع الاستعلامات المرتبطة.
  • استخدم NOT EXISTS عشان تفحص عدم وجود سجلات مرتبطة.
🔀

أنواع الربط المتقدمة (Advanced Joins)

📘 صيغة JOIN

SQL المتقدمة توفر صيغة JOIN صريحة في جملة FROM بدل مجرد سرد الجداول.

INNER JOIN

🔑 INNER JOIN (الافتراضي)

يرجع فقط الصفوف اللي فيها تطابق في الجدولين.

💡 مثال على INNER JOIN
SELECT name, First_name, Last_name FROM Course JOIN Faculty ON Course.Lecturer_id = Faculty.id; -- نفس INNER JOIN: SELECT name, First_name, Last_name FROM Course INNER JOIN Faculty ON Course.Lecturer_id = Faculty.id;

LEFT OUTER JOIN

📋 LEFT OUTER JOIN

يرجع كل الصفوف من الجدول الأيسر، حتى لو ما في تطابق في الجدول الأيمن. الصفوف اللي ما لها تطابق تجي بقيم NULL.

💡 مثال على LEFT OUTER JOIN
-- نعرض كل المقررات حتى اللي ما لها محاضرين SELECT name, First_name, Last_name FROM Course LEFT OUTER JOIN Faculty ON Course.Lecturer_id = Faculty.id; -- النتيجة راح تحتوي: Data structure | NULL | NULL

RIGHT OUTER JOIN

📋 RIGHT OUTER JOIN

يرجع كل الصفوف من الجدول الأيمن، حتى لو ما في تطابق في الجدول الأيسر.

💡 مثال على RIGHT OUTER JOIN
-- نعرض كل المحاضرين حتى اللي ما عندهم مقررات SELECT name, First_name, Last_name FROM Course RIGHT OUTER JOIN Faculty ON Course.Lecturer_id = Faculty.id; -- النتيجة راح تحتوي: NULL | Khwlah | Alrajhi

FULL OUTER JOIN

💡 مثال على FULL OUTER JOIN
-- نعرض كل المقررات وكل المحاضرين SELECT name, First_name, Last_name FROM Course FULL OUTER JOIN Faculty ON Course.Lecturer_id = Faculty.id;

NATURAL JOIN

🔑 خصائص NATURAL JOIN
  • ما نحدد شرط الربط.
  • يشتغل تلقائياً على كل الأعمدة اللي تحمل نفس الاسم.
  • كل عمود مشترك يظهر مرة وحدة في النتيجة.
💡 مثال على NATURAL JOIN
SELECT Fname, Lname, Address FROM (EMPLOYEE NATURAL JOIN (DEPARTMENT AS DEPT (Dname, Dno, Mssn, Msdate))) WHERE Dname = 'Research';

CROSS JOIN

⚠️ CROSS JOIN (الجداء الديكارتي)

استخدمه بحذر! يرجع كل التركيبات الممكنة من الصفوف.

SELECT * FROM Table1 CROSS JOIN Table2;

ربط أكثر من جدولين

💡 ربط ٣+ جداول
SELECT Pnumber, Dnum, Lname, Address, Bdate FROM ((PROJECT JOIN DEPARTMENT ON Dnum = Dnumber) JOIN EMPLOYEE ON Mgr_ssn = Ssn) WHERE Plocation = 'Stafford';
📋 تكافؤات أنواع الربط
  • LEFT JOIN = LEFT OUTER JOIN
  • RIGHT JOIN = RIGHT OUTER JOIN
  • FULL JOIN = FULL OUTER JOIN
  • INNER JOIN = JOIN
📊

دوال التجميع (Aggregation Functions)

📘 وش هي دوال التجميع؟

دوال التجميع تسوي عمليات حسابية على مجموعة من القيم وترجع قيمة واحدة.

🔑 دوال التجميع الأساسية
الدالة الوصف مثال
COUNT() عدد القيم COUNT(*) أو COUNT(DISTINCT A)
SUM() مجموع القيم SUM(Salary) أو SUM(DISTINCT Salary)
AVG() متوسط القيم AVG(GPA) أو AVG(DISTINCT age)
MAX() أعلى قيمة MAX(Salary)
MIN() أقل قيمة MIN(age)

أمثلة بسيطة على التجميع

💡 تجميع بسيط
-- متوسط GPA لكل الطلاب SELECT AVG(gpa) FROM Student; -- أعلى وأقل GPA SELECT MAX(gpa), MIN(gpa) FROM Student; -- عدد الأقسام المختلفة SELECT COUNT(DISTINCT department) FROM Student; -- إجمالي عدد الطلاب SELECT COUNT(*) FROM Student;
💡 عدة تجميعات مع بعض
-- إحصائيات الرواتب SELECT SUM(Salary), MAX(Salary), MIN(Salary), AVG(Salary) FROM EMPLOYEE; -- إحصائيات قسم Research فقط SELECT SUM(Salary), MAX(Salary), MIN(Salary), AVG(Salary) FROM EMPLOYEE JOIN DEPARTMENT ON Dno = Dnumber WHERE Dname = 'Research';

دوال التجميع في الاستعلامات المتداخلة

💡 تجميع مرتبط
-- ندوّر الموظفين اللي عندهم 2 توابع أو أكثر SELECT Lname, Fname FROM EMPLOYEE WHERE (SELECT COUNT(*) FROM DEPENDENT WHERE Ssn = Essn) >= 2;
👥

GROUP BY و HAVING

📘 وش هو GROUP BY؟

GROUP BY يقسّم البيانات إلى مجموعات بناءً على قيم الخصائص، ثم يطبق دوال التجميع على كل مجموعة بشكل منفصل.

GROUP BY أساسي

💡 أمثلة على التجميع
-- لكل قسم: عدد الموظفين ومتوسط الراتب SELECT Dno, COUNT(*), AVG(Salary) FROM EMPLOYEE GROUP BY Dno; -- متوسط GPA لكل قسم SELECT department, AVG(gpa) FROM Student GROUP BY department;
⚠️ قواعد GROUP BY
  • ما نقدر نختار في SELECT إلا الخصائص الموجودة في GROUP BY.
  • أو نقدر نختار دوال تجميع.
  • تأكد إن تقسيمك منطقي لبياناتك.

تجميع بعدة خصائص

💡 تجميع متعدد المستويات
-- نجمع حسب التخصص والمستوى الدراسي (يسوي مجموعات فرعية) SELECT major, standing, COUNT(*), AVG(age) FROM Student GROUP BY major, standing;

GROUP BY مع JOIN

💡 تجميع بعد الربط
-- لكل مشروع: عدد الموظفين اللي يشتغلون عليه SELECT Pnumber, Pname, COUNT(*) FROM PROJECT, WORKS_ON WHERE Pnumber = Pno GROUP BY Pnumber, Pname;

جملة HAVING

🔑 WHERE vs HAVING
  • WHERE: يفلتر الصفوف الفردية قبل التجميع.
  • HAVING: يفلتر المجموعات بعد التجميع وبعد تطبيق دوال التجميع.
💡 مثال على HAVING
-- المشاريع اللي فيها أكثر من 2 موظف SELECT Pnumber, Pname, COUNT(*) FROM PROJECT, WORKS_ON WHERE Pnumber = Pno GROUP BY Pnumber, Pname HAVING COUNT(*) > 2;
📋 قيم NULL في GROUP BY

إذا كان عمود التجميع فيه قيم NULL، يتم إنشاء مجموعة مستقلة لكل الصفوف اللي فيها NULL في ذلك العمود.

👁️

طرق العرض (Views)

📘 وش هو الـ View؟

طريقة العرض هي جدول افتراضي يعرض مجموعة فرعية من البيانات من جدول أو أكثر. الـ Views ما تتخزن فعلياً - هي استعلامات تولّد البيانات ديناميكياً لما نستخدمها.

🎯 أغراض الـ Views
  • تبسيط الاستعلامات المعقدة: نخفي التعقيد عن المستخدمين.
  • الأمان: نحدد الوصول لبيانات معينة.
  • التجريد: نعرض البيانات بصيغ محددة.
  • التحكم بالدخول: كل مستخدم يشوف بيانات مختلفة.

إنشاء Views

💡 View بسيط
-- View يظهر أعمدة محددة CREATE VIEW EmployeeView AS SELECT EmployeeID, Name, Position FROM Employees; -- استخدام الـ View SELECT * FROM EmployeeView;
💡 View مع JOIN
-- View يربط عدة جداول CREATE VIEW DepartmentEmployeeView AS SELECT e.Name, d.DepartmentName FROM Employees e JOIN Departments d ON e.DepartmentID = d.DepartmentID; SELECT * FROM DepartmentEmployeeView;

الـ Views المادية وغير المادية

الخاصية غير مادية (Non-Materialized) مادية (Materialized)
التخزين ما تتخزن فعلياً تتخزن كجدول مادي
التنفيذ الاستعلام ينفذ كل مرة البيانات محسوبة مسبقاً
حداثة البيانات دايمًا محدثة تحتاج تحديث دوري
الأداء أبطأ للاستعلامات المعقدة وصول أسرع
الأنسب للبيانات المتغيرة كثيراً للاستعلامات المعقدة على جداول كبيرة

تحديث Views

⚠️ قيود تحديث الـ View

مو كل الـ Views تقبل التحديث! عشان يكون الـ View قابل للتحديث:

  • لازم يرتبط مباشرة ببيانات الجدول الأصلي.
  • ما يحتوي على دوال تجميع.
  • ما يحتوي على JOIN (في أغلب الأنظمة).
  • ما يحتوي على DISTINCT.

الـ Views كوسيلة للتحكم بالدخول

🔒 الأمان مع الـ Views

الـ Views تعطينا تحكم قوي بالدخول عن طريق عرض أعمدة/صفوف محددة لمستخدمين معينين:

-- View يخفي معلومة الراتب الحساسة CREATE VIEW PublicEmployeeInfo AS SELECT EmployeeID, Name, Department, Position FROM Employees; -- عمود الراتب مو موجود في هذا الـ View
⚙️

تغيير هيكل الجداول: DROP و ALTER

DROP - حذف كائنات قاعدة البيانات

📘 جملة DROP

أمر DROP يحذف كائنات قاعدة البيانات بشكل نهائي. الكائنات المحذوفة لا يمكن استرجاعها!

⚠️ DROP نهائي!

بمجرد الحذف، الكائنات تُحذف بشكل دائم وما نستعيدها. استخدمه بحذر شديد!

💡 أمثلة على DROP
-- حذف جدول (وكل بياناته) DROP TABLE tableName; -- حذف قاعدة بيانات كاملة DROP DATABASE databaseName; -- حذف View DROP VIEW viewName;

ALTER - تعديل كائنات قاعدة البيانات

📘 جملة ALTER

أمر ALTER يعدل هيكل كائنات قاعدة البيانات الموجودة بدون حذفها.

💡 أمثلة على ALTER
-- إضافة عمود جديد ALTER TABLE tableName ADD columnName dataType; -- تعديل نوع البيانات لعمود ALTER TABLE tableName MODIFY COLUMN columnName newDataType; -- حذف عمود ALTER TABLE tableName DROP COLUMN columnName; -- إضافة قيد ALTER TABLE tableName ADD CONSTRAINT constraint_name FOREIGN KEY (column) REFERENCES otherTable(column);
🔑 DROP vs ALTER
  • DROP: يحذف الكائنات بالكامل بشكل دائم.
  • ALTER: يعدل هيكل الكائنات الموجودة.
  • كلاهما مهم لتطوير وصيانة قاعدة البيانات.
  • كلاهما يحتاج صلاحيات مناسبة.
💻

تنفيذ SQL في الكود: JDBC

📘 واجهات برمجة قواعد البيانات (APIs)
  • JDBC (Java Database Connectivity): API لتطبيقات Java.
  • DB-API: واجهة قياسية لتطبيقات Python.
  • كلها تسمح للتطبيقات تتصل، تنفذ استعلامات، تجيب النتائج، وتتعامل مع المعاملات (Transactions).
💡 مثال JDBC (Java)
import java.sql.*; public class MySQLAccess { public static void main(String[] args) { String url = "jdbc:mysql://localhost:3306/myDatabase"; String user = "username"; String password = "password"; try { // تسجيل JDBC driver Class.forName("com.mysql.cj.jdbc.Driver"); // فتح الاتصال Connection connection = DriverManager.getConnection(url, user, password); // إنشاء وتنفيذ الاستعلام Statement statement = connection.createStatement(); String sql = "SELECT id, name FROM employees"; ResultSet resultSet = statement.executeQuery(sql); // معالجة النتائج while (resultSet.next()) { int id = resultSet.getInt("id"); String name = resultSet.getString("name"); System.out.println("ID: " + id + ", Name: " + name); } // إغلاق الموارد resultSet.close(); statement.close(); connection.close(); } catch (SQLException | ClassNotFoundException e) { e.printStackTrace(); } } }
🎯 توقعات المشروع
  • تنفيذ استعلامات SELECT, INSERT, UPDATE, DELETE في الكود.
  • تطبيق منطق أساسي للتطبيق (تسجيل مستخدم، إدارة بيانات).
  • مكافأة (+١ درجة اختبار): تنفيذ واجهة رسومية كاملة.
  • مكافأة بديلة: ميزات متقدمة (أمان، triggers, deployment).
📋

هيكل استعلام SQL الكامل

🎯 صيغة استعلام SQL كاملة
SELECT <قائمة الخصائص والدوال> FROM <قائمة الجداول> [WHERE <شرط الصفوف>] [GROUP BY <خصائص التجميع>] [HAVING <شرط المجموعات>] [ORDER BY <قائمة الخصائص>];

ترتيب التنفيذ:

  1. FROM - نحدد الجداول ونعمل الربط.
  2. WHERE - نفلتر الصفوف.
  3. GROUP BY - نجمّع الصفوف.
  4. HAVING - نفلتر المجموعات.
  5. SELECT - نختار الأعمدة ونطبق الدوال.
  6. ORDER BY - نرتب النتائج النهائية.

ملخص الفصل

🎯 النقاط الرئيسية
  • قيم NULL: استخدم IS NULL/IS NOT NULL؛ افهم المنطق ثلاثي القيم.
  • الاستعلامات المتداخلة: Subqueries مع عوامل IN, ANY, ALL.
  • الاستعلامات المرتبطة: الاستعلام الداخلي يشير إلى الخارجي.
  • EXISTS/NOT EXISTS: نفحص وجود سجلات مرتبطة.
  • أنواع الربط المتقدمة: INNER, LEFT/RIGHT/FULL OUTER, NATURAL, CROSS.
  • دوال التجميع: COUNT, SUM, AVG, MAX, MIN.
  • GROUP BY: نقسم البيانات لتجميع حسب المجموعات.
  • HAVING: نفلتر المجموعات (WHERE يفلتر الصفوف).
  • طرق العرض (Views): جداول افتراضية للتبسيط والأمان.
  • تغيير الهيكل: عمليات DROP (حذف) و ALTER (تعديل).
  • JDBC: ننفذ SQL من تطبيقات Java.
🎓 وش بعد؟

بما أننا خلصنا محتوى قواعد البيانات العلائقية، المواد الجاية راح تكون عن قواعد البيانات NoSQL - نستكشف نماذج بيانات بديلة والأنظمة الموزعة!