🚀 الفصل السابع

لغة الاستعلامات الهيكلية المتقدمة

SQL المتقدمة: استعلامات متداخلة، JOINs، دوال تجميع، Views، وأكثر

📚 CS 340: مقدمة في قواعد البيانات
🎓 جامعة الأمير سلطان
👩‍🏫 إعداد: شوق العمران

📑 المحتويات

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

📌 ما هي قيمة NULL؟

NULL تعني "لا قيمة" أو "قيمة غير معروفة". ليست صفرًا ولا مسافة فارغة.

🎯 تفسيرات NULL
  • قيمة غير معروفة: مثل تاريخ ميلاد شخص غير مدخل.
  • قيمة غير متوفرة: مثل رقم هاتف شخص لا يملك هاتفًا.
  • غير منطبق: مثل حقل "المرتبة" لموظف ليس في نظام المراتب.

⚡ المنطق ثلاثي القيم في SQL

SQL لا تستخدم منطق TRUE/FALSE فقط، بل منطق ثلاثي القيم:

  • TRUE: الشرط صحيح.
  • FALSE: الشرط خطأ.
  • UNKNOWN: لا يمكن تحديد الصحة (عند وجود NULL).

جداول الحقيقة للمنطق ثلاثي القيم

AND TRUE FALSE UNKNOWN
TRUE TRUE FALSE UNKNOWN
FALSE FALSE FALSE FALSE
UNKNOWN UNKNOWN FALSE UNKNOWN
OR TRUE FALSE UNKNOWN
TRUE TRUE TRUE TRUE
FALSE TRUE FALSE UNKNOWN
UNKNOWN TRUE UNKNOWN UNKNOWN
💡 التعامل مع NULL

مهم جدًا: لا نستخدم = NULL أبدًا، بل IS NULL أو IS NOT NULL.

-- العثور على موظفين ليس لديهم مشرف (Super_ssn = NULL) SELECT Fname, Lname FROM EMPLOYEE WHERE Super_ssn IS NULL; -- العثور على موظفين لديهم مشرف SELECT Fname, Lname FROM EMPLOYEE WHERE Super_ssn IS NOT NULL;
⚠️ حقائق عن NULL
  • NULL = NULL يُنتج UNKNOWN، وليس TRUE.
  • كل NULL يُعامل كقيمة فريدة ومختلفة عن أي NULL آخر (لأغراض المقارنة).
  • عند GROUP BY، كل قيم NULL تجتمع في مجموعة واحدة.
  • دوال التجميع مثل SUM و AVG تتجاهل قيم NULL.
🔄

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

📌 تعريف

الاستعلام المتداخل هو استعلام SELECT ... FROM ... WHERE كامل داخل استعلام SQL آخر. يُستخدم لجلب بيانات تعتمد على نتيجة استعلام آخر.

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

💡 مثال: ورش العمل

المطلوب: إيجاد جميع ورش العمل التي يقدمها نفس مقدم ورشة "Robot Operating System".

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

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

الطريقة 2: استعلام واحد متداخل

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

استخدام IN

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

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

استخدام SOME / ANY و ALL

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

مجموعات صريحة (Explicit Sets)

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

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

📌 تعريف

استعلام داخلي يعتمد على متغير من الاستعلام الخارجي. يُنفذ مرة لكل صف من الاستعلام الخارجي.

⚙️ آلية العمل

شبهه بـ حلقة (Loop):

  1. نأخذ صفًا من الاستعلام الخارجي.
  2. ننفذ الاستعلام الداخلي باستخدام قيمة من هذا الصف.
  3. إذا تحقق الشرط، نضيف الصف للنتائج.
  4. ننتقل للصف التالي، وهكذا.
💡 مثال: موظف له تابع بنفس الاسم والجنس
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.Fname و E.Sex من الاستعلام الخارجي.

🔄 بديل باستخدام JOIN

يمكن إعادة كتابة بعض الاستعلامات المرتبطة باستخدام JOIN:

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

EXISTS / NOT EXISTS / UNIQUE

📌 EXISTS

EXISTS يُرجع TRUE إذا كان الاستعلام الداخلي يُرجع صفًا واحدًا على الأقل.

NOT EXISTS يُرجع TRUE إذا كان الاستعلام الداخلي لا يُرجع أي صفوف.

💡 EXISTS
-- موظف له تابع بنفس الاسم والجنس SELECT E.Fname, E.Lname FROM EMPLOYEE E WHERE EXISTS (SELECT * FROM DEPENDENT 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);
📌 UNIQUE

UNIQUE يُرجع TRUE إذا لم يكن هناك صفوف مكررة في نتيجة الاستعلام الداخلي. (نادر الاستخدام).

🎯 متى نستخدم EXISTS؟
  • لاختبار وجود علاقة بين الجداول.
  • أداؤه أفضل من IN في بعض قواعد البيانات.
  • مع الاستعلامات المرتبطة، هو الخيار المنطقي.
🔀

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

📌 صيغة JOIN الصريحة

SQL تقدم صيغة JOIN في جملة FROM بدلاً من كتابة الشروط في WHERE.

1. INNER JOIN

INNER JOIN = JOIN الافتراضي. يُرجع الصفوف المتطابقة في كلا الجدولين فقط.

SELECT name, First_name, Last_name FROM Course JOIN Faculty ON Course.Lecturer_id = Faculty.id;

2. LEFT OUTER JOIN

LEFT OUTER JOIN (أو LEFT JOIN) يُرجع كل الصفوف من الجدول الأيسر، مع بيانات الجدول الأيمن إن وُجدت، أو NULL إن لم توجد.

-- كل المقررات حتى التي ليس لها محاضر SELECT name, First_name, Last_name FROM Course LEFT OUTER JOIN Faculty ON Course.Lecturer_id = Faculty.id;

3. RIGHT OUTER JOIN

RIGHT OUTER JOIN يُرجع كل الصفوف من الجدول الأيمن.

-- كل المحاضرين حتى الذين ليس لديهم مقررات SELECT name, First_name, Last_name FROM Course RIGHT OUTER JOIN Faculty ON Course.Lecturer_id = Faculty.id;

4. FULL OUTER JOIN

FULL OUTER JOIN يُرجع كل الصفوف من كلا الجدولين، مع NULL في الجانب الآخر إن لم يوجد تطابق.

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

5. NATURAL JOIN

NATURAL JOIN يربط الجداول بناءً على الأعمدة التي تحمل نفس الاسم تلقائيًا. كل عمود مشترك يظهر مرة واحدة فقط في النتيجة.

SELECT Fname, Lname, Address FROM (EMPLOYEE NATURAL JOIN (DEPARTMENT AS DEPT (Dname, Dno, Mssn, Msdate))) WHERE Dname = 'Research';

6. CROSS JOIN

⚠️ الجداء الديكارتي

CROSS JOIN يُرجع كل التركيبات الممكنة من الصفوف من كلا الجدولين. استخدمه بحذر شديد لأنه قد يُنتج كميات هائلة من البيانات.

SELECT * FROM Table1 CROSS JOIN Table2;

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

💡 مثال ربط 3 جداول
SELECT Pnumber, Dnum, Lname, Address, Bdate FROM ((PROJECT JOIN DEPARTMENT ON Dnum = Dnumber) JOIN EMPLOYEE ON Mgr_ssn = Ssn) WHERE Plocation = 'Stafford';
📊

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

📌 تعريف

دوال التجميع تُجري عمليات حسابية على مجموعة من القيم وتُرجع قيمة واحدة. تتجاهل NULL في العمليات (ما عدا COUNT(*)).

الدالة الوصف مثال
COUNT(*) عدد الصفوف في الجدول (بما فيها NULL). COUNT(*)
COUNT(column) عدد القيم غير NULL في العمود. COUNT(DISTINCT Dept)
SUM(column) مجموع القيم في العمود (يجب أن يكون رقميًا). SUM(Hours)
AVG(column) متوسط القيم في العمود. AVG(Salary)
MAX(column) أكبر قيمة. MAX(GPA)
MIN(column) أصغر قيمة. 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;
👥

GROUP BY و HAVING

📌 GROUP BY

يقسم الصفوف إلى مجموعات بناءً على قيم عمود أو أكثر، ثم يطبق دوال التجميع على كل مجموعة.

⚠️ قاعدة مهمة

في SELECT، يمكننا فقط اختيار الأعمدة الموجودة في GROUP BY أو دوال التجميع.

💡 تجميع حسب قسم
-- لكل قسم: عدد الموظفين ومتوسط الراتب SELECT Dno, COUNT(*), AVG(Salary) FROM EMPLOYEE GROUP BY Dno; -- لكل تخصص ومستوى: عدد الطلاب ومتوسط العمر 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 يُطبق قبل التجميع، ويفلتر الصفوف.

HAVING يُطبق بعد التجميع، ويفلتر المجموعات (باستخدام دوال التجميع).

💡 مثال HAVING
-- المشاريع التي يعمل بها أكثر من 2 موظف SELECT Pnumber, Pname, COUNT(*) FROM PROJECT, WORKS_ON WHERE Pnumber = Pno GROUP BY Pnumber, Pname HAVING COUNT(*) > 2;
👁️

طرق العرض (Views)

📌 تعريف

طريقة العرض (View) هي جدول افتراضي يمثل نتيجة استعلام مخزّن. لا تحتوي على بيانات حقيقية (في الغالب) بل تُنشئ البيانات عند الاستعلام.

🎯 فوائد الـ Views
  • تبسيط الاستعلامات: إخفاء التعقيد عن المستخدمين.
  • الأمان: عرض جزء من البيانات فقط، وإخفاء الأعمدة الحساسة (مثل الراتب).
  • اتساق البيانات: توفير واجهة موحدة للبيانات.

إنشاء واستخدام View

💡 View بسيط
-- إنشاء View للمعلومات العامة للموظفين (بدون راتب) CREATE VIEW EmployeePublic AS SELECT Ssn, Fname, Lname, Address, Sex FROM EMPLOYEE; -- استخدام الـ View SELECT * FROM EmployeePublic;
💡 View مع JOIN
CREATE VIEW DeptInfo AS SELECT Dname, COUNT(*) AS EmpCount, AVG(Salary) AS AvgSalary FROM DEPARTMENT, EMPLOYEE WHERE Dnumber = Dno GROUP BY Dname;

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

الخاصية غير مادية (Virtual) مادية (Materialized)
التخزين لا تخزن بيانات، فقط الاستعلام. تخزن البيانات فعليًا على القرص.
السرعة أبطأ (يُنفذ الاستعلام كل مرة). سريع جدًا (بيانات جاهزة).
حداثة البيانات دائمًا محدثة. تحتاج تحديثًا دوريًا.
الاستخدام الـ Views العادية في SQL. تُستخدم في مستودعات البيانات (Data Warehouses).
⚠️ تحديث Views

ليس كل View قابل للتحديث (INSERT/UPDATE/DELETE). الشروط عادة:

  • يجب أن يكون مأخوذًا من جدول واحد.
  • يجب أن يحتوي على المفتاح الرئيسي للجدول.
  • يجب ألا يحتوي على دوال تجميع أو DISTINCT أو GROUP BY.
⚙️

تعديل هيكل قاعدة البيانات: DROP و ALTER

DROP: الحذف النهائي

⚠️ DROP نهائي ولا يمكن التراجع عنه

يُستخدم لحذف كائنات قاعدة البيانات بشكل كامل.

-- حذف جدول (وجميع بياناته) DROP TABLE Student; -- حذف قاعدة بيانات DROP DATABASE UniversityDB; -- حذف View DROP VIEW EmployeePublic;

ALTER: تعديل الهيكل

📌 ALTER TABLE

يُستخدم لإضافة أو تعديل أو حذف أعمدة أو قيود في جدول موجود.

-- إضافة عمود جديد ALTER TABLE Student ADD phoneNumber VARCHAR(15); -- تعديل نوع عمود ALTER TABLE Student MODIFY COLUMN phoneNumber VARCHAR(20); -- حذف عمود ALTER TABLE Student DROP COLUMN phoneNumber; -- إضافة مفتاح خارجي ALTER TABLE Student ADD CONSTRAINT fk_department FOREIGN KEY (dept_id) REFERENCES Department(id);
💻

JDBC و APIs قواعد البيانات

📌 ما هي JDBC؟

JDBC (Java Database Connectivity) هي واجهة برمجة تطبيقات (API) تسمح لتطبيقات Java بالاتصال بقواعد البيانات وتنفيذ استعلامات SQL.

💡 مثال JDBC
import java.sql.*; public class DBExample { public static void main(String[] args) { String url = "jdbc:mysql://localhost:3306/myDB"; String user = "root"; String password = "pass"; try { // 1. تحميل Driver Class.forName("com.mysql.cj.jdbc.Driver"); // 2. فتح اتصال Connection conn = DriverManager.getConnection(url, user, password); // 3. إنشاء وتنفيذ استعلام Statement stmt = conn.createStatement(); String sql = "SELECT id, name FROM students"; ResultSet rs = stmt.executeQuery(sql); // 4. معالجة النتائج while (rs.next()) { int id = rs.getInt("id"); String name = rs.getString("name"); System.out.println(id + ": " + name); } // 5. إغلاق الموارد rs.close(); stmt.close(); conn.close(); } catch (Exception e) { e.printStackTrace(); } } }
🎯 خطوات JDBC الأساسية
  1. تحميل الـ Driver المناسب.
  2. فتح اتصال (Connection).
  3. إنشاء كائن Statement.
  4. تنفيذ الاستعلام (executeQuery أو executeUpdate).
  5. معالجة ResultSet (إذا كان استعلام SELECT).
  6. إغلاق الاتصال والموارد.
📋

هيكل استعلام SQL الكامل وترتيب التنفيذ

📝 الصيغة الكاملة
SELECT <قائمة الأعمدة ودوال التجميع> FROM <قائمة الجداول> [WHERE <شرط الصفوف>] [GROUP BY <أعمدة التجميع>] [HAVING <شرط المجموعات>] [ORDER BY <أعمدة الترتيب>];
⚙️ ترتيب التنفيذ المنطقي (Logical Order)
  1. FROM / JOIN: تحديد الجداول وربطها.
  2. WHERE: فلترة الصفوف الفردية.
  3. GROUP BY: تجميع الصفوف.
  4. HAVING: فلترة المجموعات.
  5. SELECT: حساب الأعمدة ودوال التجميع.
  6. ORDER BY: ترتيب النتائج النهائية.

ملاحظة: ترتيب الكتابة يختلف عن ترتيب التنفيذ. هذا مهم جدًا لفهم الأخطاء.

ملخص الفصل السابع

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