مقدمة في قواعد البيانات: استعلامات SQL المتقدمة والقيود
تغطي هذه الوحدة استعلامات SQL المعقدة بما في ذلك التعامل مع القيم الفارغة (NULL)، المنطق ثلاثي القيم، الاستعلامات المتداخلة، أنواع الربط (Joins)، دوال التجميع، التجميع (Grouping)، بالإضافة إلى تحديد القيود كتوكيدات (Assertions) والإجراءات كمشغلات (Triggers).
Introduction to Database: More SQL-1 (Complex Queries, Triggers, Views)
This module covers complex SQL retrieval queries including handling NULLs, 3-valued logic, nested queries, joins, aggregate functions, grouping, as well as specifying constraints as assertions and actions as triggers.
أهداف التعلم
- كتابة استعلامات SQL معقدة لاسترجاع السجلات.
- تطبيق القيود (Constraints) والمشغلات (Triggers) في SQL.
- فهم واستخدام المنطق ثلاثي القيم للتعامل مع القيم الفارغة (NULL).
- التمييز بين الاستعلامات المتداخلة المترابطة وغير المترابطة.
- Write complex SQL queries for retrieval of records.
- Apply constraints and triggers in SQL.
- Understand and utilize 3-valued logic to handle NULL values.
- Differentiate between correlated and uncorrelated nested queries.
1 القيم الفارغة (NULL) والمنطق ثلاثي القيم
1 Handling NULLs and 3-valued Logic
تستخدم SQL المنطق ثلاثي القيم (صحيح، خاطئ، غير معروف) للتعامل مع البيانات المفقودة، تماماً كإجابة 'لا أعلم' في استبيان.
SQL uses 3-valued logic (TRUE, FALSE, UNKNOWN) to handle missing data, much like answering 'I don't know' on a survey.
تسمح SQL بالاستعلامات التي تتحقق مما إذا كانت السمة NULL (مفقودة أو غير محددة أو غير قابلة للتطبيق).
تستخدم SQL المعاملات IS أو IS NOT لمقارنة سمة بـ NULL لأنها تعتبر كل قيمة NULL مميزة عن قيم NULL الأخرى، لذا فإن مقارنة المساواة (=) غير مناسبة.
يؤدي وجود NULL إلى تقييم الشرط إلى UNKNOWN، مما يؤدي إلى منطق ثلاثي القيم. عند دمج الشروط باستخدام AND و OR و NOT، يجب أخذ UNKNOWN في الاعتبار.
SQL allows queries that check if an attribute is NULL (missing, undefined, or not applicable).
SQL uses IS or IS NOT to compare an attribute to NULL because it considers each NULL value distinct from other NULL values, so equality comparison (=) is not appropriate.
With NULLs, a condition can evaluate to UNKNOWN, leading to 3-valued logic. Combining individual conditions using AND, OR, NOT logical connectives must consider UNKNOWN in addition to TRUE and FALSE.
تم تصميم المنطق ثلاثي القيم للحفاظ على سلامة العمليات العلائقية عند غياب البيانات.
على سبيل المثال، في جدول الحقيقة، (TRUE AND UNKNOWN) يعطي UNKNOWN، بينما (TRUE OR UNKNOWN) يعطي TRUE. هذا يمنع قاعدة البيانات من استبعاد أو تضمين سجلات بشكل خاطئ بناءً على افتراضات حول البيانات المفقودة.
3-valued logic is designed to maintain the integrity of relational operations when data is absent.
For example, in the truth table, (TRUE AND UNKNOWN) yields UNKNOWN, while (TRUE OR UNKNOWN) yields TRUE. This prevents the database from falsely excluding or including records based on assumptions about missing data.
SELECT FNAME, LNAME
FROM EMPLOYEE
WHERE SUPERSSN IS NULL;
لماذا لا يمكننا استخدام المعامل '=' للتحقق من القيم الفارغة (NULL)؟ Why can't we use the '=' operator to check for NULL values?
لأن كل قيمة NULL تعتبر مميزة وغير معروفة، فمقارنة مجهول بمجهول آخر لا تعني بالضرورة أنهما متساويان. لذلك نستخدم IS NULL.
Because each NULL is considered distinct and unknown. Comparing one unknown to another unknown does not guarantee they are equal. Therefore, IS NULL is used.
2 الاستعلامات المتداخلة (Nested Queries)
2 Nested Queries
استعلام كامل يوضع داخل استعلام آخر (عادة في جملة WHERE)، كصندوق داخل صندوق.
A complete query placed inside another query (usually in the WHERE clause), like a box inside a box.
يمكن تحديد استعلام SELECT كامل، يسمى الاستعلام المتداخل (nested query)، داخل جملة WHERE لاستعلام آخر (الاستعلام الخارجي). يمكن أن يكون هناك عدة مستويات من التداخل.
المعامل IN يقارن قيمة بمجموعة من القيم ويقيم إلى TRUE إذا كانت القيمة أحد عناصر المجموعة.
تنقسم الاستعلامات المتداخلة إلى نوعين: غير مترابطة (تُنفذ مرة واحدة بشكل مستقل) ومترابطة (Correlated) حيث يشير الاستعلام الداخلي إلى سمة من الاستعلام الخارجي، مما يعني أنه يُنفذ لكل صف في الاستعلام الخارجي.
A complete SELECT query, called a nested query, can be specified within the WHERE-clause of another query (the outer query). There can be several levels of nested queries.
The comparison operator IN compares a value with a set of values and evaluates to TRUE if the value is in the set.
Nested queries are divided into uncorrelated (executed once independently) and correlated, where the inner query references an attribute from the outer query, meaning it executes for each tuple of the outer query.
الاستعلامات المترابطة (Correlated) مكلفة حسابياً لأن تعقيدها الزمني هو O(N*M) حيث يتم تنفيذ الاستعلام الداخلي لكل صف في الاستعلام الخارجي.
في المقابل، الاستعلامات غير المترابطة تُنفذ مرة واحدة فقط. يمكن غالباً إعادة كتابة الاستعلامات المترابطة باستخدام الربط (Joins) لتحسين الأداء.
Correlated queries are computationally expensive because their time complexity is O(N*M), as the inner query is evaluated for every row of the outer query.
Uncorrelated queries, however, are evaluated only once. Correlated queries can often be rewritten using Joins to optimize performance.
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);
| الاستعلام المتداخل المترابط Correlated Nested Query | الاستعلام المتداخل غير المترابط Uncorrelated Nested Query | |
|---|---|---|
| الاعتمادية Dependency | يعتمد على الاستعلام الخارجي (يشير إلى سماته) Dependent on outer query (references its attributes) | مستقل تماماً عن الاستعلام الخارجي Completely independent of the outer query |
| تكرار التنفيذ Execution Frequency | يُنفذ مرة واحدة لكل صف في الاستعلام الخارجي Executed once for each tuple in the outer query | يُنفذ مرة واحدة فقط Executed only once |
كيف يمكن تحويل الاستعلام المترابط السابق إلى استعلام باستخدام الربط (Join)؟ How can the previous correlated query be converted into a Join query?
يمكن كتابته كاستعلام كتلة واحدة: SELECT E.FNAME, E.LNAME FROM EMPLOYEE E, DEPENDENT D WHERE E.SSN=D.ESSN AND E.FNAME=D.DEPENDENT_NAME;
It can be written as a single block query: SELECT E.FNAME, E.LNAME FROM EMPLOYEE E, DEPENDENT D WHERE E.SSN=D.ESSN AND E.FNAME=D.DEPENDENT_NAME;
3 دالة EXISTS
3 The EXISTS Function
دالة منطقية تتحقق مما إذا كان الاستعلام الفرعي يرجع أي صفوف (ليست فارغة).
A boolean function that checks whether a subquery returns any rows (is not empty).
تُستخدم EXISTS للتحقق مما إذا كانت نتيجة الاستعلام فارغة (لا تحتوي على صفوف) أو لا (تحتوي على صف واحد أو أكثر). تُطبق على استعلام، ولكنها تُرجع نتيجة منطقية (TRUE أو FALSE).
يمكن استخدامها في جملة WHERE كشرط. تُقيم EXISTS (Q) إلى TRUE إذا كانت نتيجة Q تحتوي على صف واحد أو أكثر؛ وتُقيم إلى FALSE إذا لم تكن هناك صفوف.
تُستخدم NOT EXISTS للتحقق من عدم وجود صفوف.
EXISTS is used to check whether the result of a query is empty (contains no tuples) or not (contains one or more tuples). It is applied to a query but returns a boolean result (TRUE or FALSE).
It can be used in the WHERE-clause as a condition. EXISTS (Q) evaluates to TRUE if the result of Q has one or more tuples; evaluates to FALSE if the result of Q has no tuples.
NOT EXISTS is used to check for emptiness.
تعتبر EXISTS ضرورية للقوة التعبيرية لـ SQL، خاصة عند محاكاة عملية التقسيم (Division) في الجبر العلائقي.
كما أنها فعالة جداً لأن محرك قاعدة البيانات يتوقف عن البحث بمجرد العثور على أول تطابق (Short-circuit evaluation)، بدلاً من حساب جميع السجلات كما تفعل دالة COUNT.
EXISTS is necessary for the expressive power of SQL, especially when simulating the relational algebra division operation.
It is also highly efficient because the database engine stops searching as soon as it finds the first match (short-circuit evaluation), rather than counting all records like COUNT does.
SELECT E.FNAME, E.LNAME
FROM EMPLOYEE E
WHERE NOT EXISTS (SELECT *
FROM DEPENDENT D
WHERE E.SSN=D.ESSN);
أيهما أسرع عادةً: استخدام EXISTS أم استخدام COUNT(*) > 0؟ ولماذا؟ Which is generally faster: using EXISTS or COUNT(*) > 0? Why?
EXISTS أسرع لأنها تتوقف عن البحث بمجرد العثور على أول سجل يطابق الشرط، بينما COUNT(*) تضطر للمرور على جميع السجلات لعدها.
EXISTS is faster because it short-circuits and stops searching as soon as it finds the first matching record, whereas COUNT(*) must scan and count all matching records.
4 الجداول المترابطة (Joined Tables)
4 Joined Tables (Relations)
دمج أعمدة من جدولين أو أكثر بناءً على عمود مشترك بينهما.
Combining columns from two or more tables based on a related column between them.
يمكن تحديد 'علاقة مترابطة' في جملة FROM. يسمح ذلك للمستخدم بتحديد أنواع مختلفة من الربط: INNER JOIN (الربط العادي حيث يجب أن تستوفي الصفوف المترابطة جميع شروط الربط)، OUTER JOIN (يسمح بتضمين السجلات التي ليس لها تطابق عن طريق حشوها بقيم NULL)، و NATURAL JOIN (ربط ضمني حيث يتم دمج الأعمدة التي لها نفس الاسم تلقائياً).
ينقسم الربط الخارجي إلى LEFT (يحتفظ بكل سجلات الجدول الأيسر)، RIGHT (يحتفظ بكل سجلات الجدول الأيمن)، و FULL (يحتفظ بكل السجلات من كلا الجدولين).
A 'joined relation' can be specified in the FROM-clause. It allows the user to specify different types of joins: INNER JOIN (regular join where joined tuples must satisfy all join conditions), OUTER JOIN (includes records without matches by padding them with NULLs), and NATURAL JOIN (implicit join where attributes with the same name are automatically matched).
Outer joins are divided into LEFT (keeps all left table records), RIGHT (keeps all right table records), and FULL (keeps all records from both tables).
يحل الربط الخارجي (Outer Join) مشكلة فقدان المعلومات في الربط الداخلي (Inner Join).
على سبيل المثال، إذا أردنا عرض جميع الأقسام حتى تلك التي لا تحتوي على موظفين، فإن الربط الداخلي سيتجاهلها، بينما الربط الخارجي الأيسر (إذا كان جدول الأقسام على اليسار) سيحتفظ بها ويضع NULL في بيانات الموظفين.
Outer joins solve the information loss problem inherent in Inner Joins.
For instance, if we want to display all departments, even those with no employees, an Inner Join would drop them. A Left Outer Join (with Department on the left) retains them, placing NULLs in the employee columns.
SELECT E.FNAME, E.LNAME, D.DNAME
FROM (DEPARTMENT AS D LEFT OUTER JOIN EMPLOYEE AS E ON D.DNUMBER=E.DNO);
ماذا يحدث إذا استخدمنا NATURAL JOIN بين جدولين لا يحتويان على أي أعمدة بنفس الاسم؟ What happens if we use a NATURAL JOIN between two tables that have no columns with the same name?
سيتحول الربط إلى ضرب ديكارتي (Cross Join)، حيث سيتم دمج كل صف من الجدول الأول مع كل صف من الجدول الثاني.
The join degrades into a Cartesian product (Cross Join), where every row from the first table is combined with every row from the second table.
5 دوال التجميع والتجميع (Grouping)
5 Aggregate Functions and Grouping
دوال تلخص بيانات عدة صفوف في قيمة واحدة (مثل المجموع أو المتوسط)، وغالباً ما تُقسم البيانات إلى مجموعات باستخدام GROUP BY.
Functions that summarize data from multiple rows into a single value (like sum or average), often partitioning data into groups using GROUP BY.
تشمل دوال التجميع COUNT و SUM و MAX و MIN و AVG. يمكنها تلخيص المعلومات من عدة صفوف في صف واحد.
في كثير من الحالات، نريد تطبيق هذه الدوال على مجموعات فرعية من الصفوف. تتكون كل مجموعة من الصفوف التي لها نفس القيمة لسمة التجميع (مثل الموظفين في نفس القسم).
تحتوي SQL على جملة GROUP BY لتحديد سمات التجميع. إذا أردنا تصفية هذه المجموعات بناءً على شرط معين، نستخدم جملة HAVING (بدلاً من WHERE التي تصفي الصفوف الفردية).
Aggregate functions include COUNT, SUM, MAX, MIN, and AVG. They summarize information from multiple tuples into a single tuple.
Often, we apply these functions to subgroups of tuples. Each subgroup consists of tuples that have the same value for the grouping attribute(s) (e.g., employees in the same department).
SQL uses the GROUP BY clause to specify grouping attributes. To filter these groups based on a condition, the HAVING clause is used (unlike WHERE, which filters individual tuples).
الترتيب المفاهيمي لتقييم الاستعلام هو: FROM (تحديد الجداول) -> WHERE (تصفية الصفوف) -> GROUP BY (تكوين المجموعات) -> HAVING (تصفية المجموعات) -> SELECT (استخراج الأعمدة والدوال) -> ORDER BY (ترتيب النتائج).
هذا يفسر لماذا لا يمكن استخدام دوال التجميع في جملة WHERE.
The conceptual evaluation order of a query is: FROM -> WHERE -> GROUP BY -> HAVING -> SELECT -> ORDER BY.
This explains why aggregate functions cannot be used in the WHERE clause, as WHERE is evaluated before groups are formed and aggregates are calculated.
SELECT PNUMBER, PNAME, COUNT(*)
FROM PROJECT, WORKS_ON
WHERE PNUMBER=PNO
GROUP BY PNUMBER, PNAME
HAVING COUNT(*) > 2;
| جملة WHERE WHERE Clause | جملة HAVING HAVING Clause | |
|---|---|---|
| مستوى التصفية Filtering Level | يصفي الصفوف الفردية Filters individual tuples (rows) | يصفي المجموعات بأكملها Filters entire groups |
| استخدام دوال التجميع Use of Aggregate Functions | لا يمكن استخدام دوال التجميع داخله Cannot contain aggregate functions | يُستخدم عادةً مع دوال التجميع Typically used with aggregate functions |
لماذا يجب أن تظهر أي سمة موجودة في جملة SELECT (وليست داخل دالة تجميع) في جملة GROUP BY؟ Why must any attribute in the SELECT clause (that is not inside an aggregate function) also appear in the GROUP BY clause?
لأن التجميع يدمج عدة صفوف في صف واحد. إذا لم تكن السمة جزءاً من مفتاح التجميع، فلن تعرف قاعدة البيانات أي قيمة من الصفوف المدمجة يجب أن تعرضها.
Because grouping collapses multiple rows into one. If an attribute is not part of the grouping key, the database wouldn't know which specific row's value to display for that group.
6 التوكيدات (Assertions)
6 Assertions
قواعد عامة تُطبق على قاعدة البيانات بأكملها لضمان عدم انتهاك شروط معينة أبداً.
General rules applied to the entire database to ensure specific conditions are never violated.
التوكيدات هي قيود عامة لا تتناسب مع قيود SQL الأساسية (مثل المفاتيح الأساسية، UNIQUE، NOT NULL). يتم إنشاؤها باستخدام CREATE ASSERTION.
تتكون من اسم القيد، متبوعاً بـ CHECK، ثم شرط يجب أن يكون دائماً TRUE.
لاستخدامها، نحدد استعلاماً ينتهك الشرط ونضعه داخل جملة NOT EXISTS، بحيث يجب أن تكون نتيجة الاستعلام فارغة دائماً.
Assertions are general constraints that do not fit into basic SQL constraints (primary keys, UNIQUE, foreign keys, NOT NULL). They are created using CREATE ASSERTION.
Components include a constraint name, followed by CHECK, followed by a condition that must be TRUE.
To use them, specify a query that violates the condition inside a NOT EXISTS clause; the query result must be empty for the assertion to hold.
رغم قوة التوكيدات في الحفاظ على سلامة البيانات المعقدة (مثل مقارنة رواتب الموظفين بمديريهم)، إلا أنها غير مدعومة بشكل واسع في العديد من أنظمة إدارة قواعد البيانات التجارية (RDBMS) بسبب التكلفة العالية للتحقق منها عند كل عملية تعديل.
بدلاً من ذلك، تُستخدم المشغلات (Triggers) غالباً لتحقيق نفس الهدف.
While assertions are powerful for maintaining complex data integrity (e.g., ensuring an employee's salary doesn't exceed their manager's), they are not widely supported in many commercial RDBMS due to the high performance overhead of checking them on every modification.
Triggers are often used as a practical alternative.
CREATE ASSERTION SALARY_CONSTRAINT
CHECK (NOT EXISTS (SELECT *
FROM EMPLOYEE E, EMPLOYEE M, DEPARTMENT D
WHERE E.SALARY > M.SALARY AND
E.DNO=D.DNUMBER AND
D.MGRSSN=M.SSN));
لماذا نستخدم NOT EXISTS داخل جملة CHECK في التوكيدات؟ Why do we use NOT EXISTS inside the CHECK clause of an assertion?
لأننا نصيغ الاستعلام للبحث عن الحالات التي تنتهك القاعدة. إذا لم تكن هناك أي سجلات تنتهك القاعدة (NOT EXISTS)، فهذا يعني أن قاعدة البيانات في حالة سليمة (الشرط TRUE).
Because we formulate the query to find violations of the rule. If no such violating records exist (NOT EXISTS), the database is in a valid state (the condition evaluates to TRUE).
7 المشغلات (SQL Triggers)
7 SQL Triggers
أكواد تُنفذ تلقائياً (تُستدعى) استجابةً لحدث معين في قاعدة البيانات (مثل الإضافة أو التعديل).
Code that automatically executes (fires) in response to a specific event on a particular table (like INSERT or UPDATE).
تُستخدم المشغلات لمراقبة قاعدة البيانات وبدء إجراء عند حدوث أحداث وشروط معينة.
تتكون من ثلاثة أجزاء (ECA): الحدث (Event) مثل عملية إدراج أو حذف أو تحديث؛ الشرط (Condition) الذي يجب أن يتحقق؛ والإجراء (Action) الذي يُتخذ عند تحقق الشرط.
يمكن تحديد وقت التشغيل (BEFORE أو AFTER) وما إذا كان يُنفذ لكل صف (FOR EACH ROW).
Triggers are used to monitor a database and initiate action when certain events and conditions occur.
They follow the ECA model: Event (such as an insert, delete, or update operation), Condition (to be checked), and Action (to be taken when the condition is satisfied).
They can be specified to run BEFORE or AFTER the event, and can operate FOR EACH ROW modified.
المشغلات مفيدة جداً لفرض قواعد العمل المعقدة، وتتبع التغييرات (Auditing)، وحساب القيم المشتقة.
ومع ذلك، يجب استخدامها بحذر لأنها يمكن أن تسبب تأثيرات متسلسلة (Cascading triggers) حيث يقوم مشغل بتفعيل مشغل آخر، مما يؤدي إلى تعقيد تتبع الأخطاء ومشاكل في الأداء.
Triggers are highly useful for enforcing complex business rules, auditing changes, and calculating derived values.
However, they must be used cautiously as they can cause cascading effects (one trigger firing another), leading to debugging nightmares and performance degradation.
CREATE TRIGGER INFORM_SUPERVISOR
BEFORE INSERT OR UPDATE OF SALARY, SUPERVISOR_SSN ON EMPLOYEE
FOR EACH ROW
WHEN (NEW.SALARY > (SELECT SALARY FROM EMPLOYEE WHERE SSN=NEW.SUPERVISOR_SSN))
INFORM_SUPERVISOR (NEW.SUPERVISOR_SSN, NEW.SSN);
ما الفرق بين المشغل (Trigger) والتوكيد (Assertion)؟ What is the difference between a Trigger and an Assertion?
التوكيد هو قيد يمنع أي عملية تجعل الشرط خاطئاً. المشغل هو إجراء نشط يُنفذ كوداً معيناً استجابة لحدث، ويمكن استخدامه لتعديل البيانات أو إرسال تنبيهات، وليس فقط لمنع العمليات.
An assertion is a passive constraint that simply blocks any transaction violating its condition. A trigger is active; it executes specific code in response to an event, and can modify data or send alerts, not just block transactions.