القيود والمفاتيح
القيود والمفاتيح
🌍 تشبيه من العالم الحقيقي
فكر في قاعدة البيانات كمكتب صارم:
- PRIMARY KEY = رقم هوية الموظف لكل شخص، لا يتكرر أبدًا، لا يكون فارغًا أبدًا
- FOREIGN KEY = الموظف يجب أن ينتمي إلى قسم موجود فعليًا
- UNIQUE = البريد الإلكتروني لكل شخص يجب أن يكون فريدًا
- NOT NULL = حقل الاسم لا يمكن تركه فارغًا
- CHECK = العمر يجب أن يكون بين 18 و65
- DEFAULT = عندما لا يتم ملء الحالة، يكون الافتراضي "نشط"
القيود هي "حراس القواعد" في قاعدة البيانات — تتحقق تلقائيًا من البيانات أثناء الكتابة لمنع دخول البيانات القذرة.
🎯 المفاهيم الأساسية
PRIMARY KEY
يُعرّف بشكل فريد كل صف في الجدول. لا يسمح بالتكرار، لا يسمح بـ NULL. يمكن أن يحتوي الجدول على مفتاح أساسي واحد فقط.
CREATE TABLE employees (
employee_id INT PRIMARY KEY,
first_name VARCHAR(50) NOT NULL
);
يمكن أن يتكون المفتاح الأساسي أيضًا من عدة أعمدة (مفتاح أساسي مركب):
CREATE TABLE order_items (
order_id INT,
product_id INT,
quantity INT,
PRIMARY KEY (order_id, product_id)
);
FOREIGN KEY
يضمن أن القيم في جدول واحد يجب أن توجد في جدول آخر، مما يحافظ على سلامة المراجع.
CREATE TABLE employees (
employee_id INT PRIMARY KEY,
department_id INT,
FOREIGN KEY (department_id) REFERENCES departments(department_id)
);
عمليات الت.Cascade للمفتاح الخارجي:
| العملية | الوصف |
|---|---|
ON DELETE CASCADE |
عند حذف صف الجدول الأب، يتم حذف الصفوف المرتبطة في الجدول الابن أيضًا |
ON DELETE SET NULL |
عند حذف صف الجدول الأب، يتم تعيين المفتاح الخارجي في الجدول الابن إلى NULL |
ON DELETE RESTRICT |
يُمنع الحذف إذا كانت هناك مراجع موجودة (افتراضي) |
ON UPDATE CASCADE |
عند تحديث المفتاح الأساسي في الجدول الأب، يتم تحديث المفتاح الخارجي في الجدول الابن accordingly |
FOREIGN KEY (department_id) REFERENCES departments(department_id)
ON DELETE SET NULL
ON UPDATE CASCADE;
قيد UNIQUE
يضمن أن جميع القيم في العمود غير مكررة، لكنه يسمح بـ NULL (في معظم قواعد البيانات، يُسمح بعدة قيم NULL).
CREATE TABLE employees (
employee_id INT PRIMARY KEY,
email VARCHAR(100) UNIQUE
);
الاختلافات بين UNIQUE و PRIMARY KEY:
| الميزة | PRIMARY KEY | UNIQUE |
|---|---|---|
| قيم NULL | ❌ غير مسموح | ✅ مسموح |
| العدد | واحد فقط لكل جدول | يمكن أن يكون عدة |
| الغرض | مُعرّف فريد للصف | فريدية قيمة العمود |
قيد NOT NULL
يضمن أن العمود لا يمكن أن يخزن قيم NULL.
CREATE TABLE employees (
employee_id INT PRIMARY KEY,
first_name VARCHAR(50) NOT NULL,
last_name VARCHAR(50) NOT NULL
);
قيد CHECK
يضمن أن القيم في العمود تفي بشرط محدد.
CREATE TABLE employees (
employee_id INT PRIMARY KEY,
salary DECIMAL(10,2) CHECK (salary > 0),
age INT CHECK (age >= 18 AND age <= 65),
gender CHAR(1) CHECK (gender IN ('M', 'F'))
);
القيمة الافتراضية (DEFAULT)
عند إدراج بيانات بدون تحديد قيمة للعمود، يتم استخدام القيمة الافتراضية تلقائيًا.
CREATE TABLE employees (
employee_id INT PRIMARY KEY,
status VARCHAR(20) DEFAULT 'active',
hire_date DATE DEFAULT CURRENT_DATE,
salary DECIMAL(10,2) DEFAULT 5000.00
);
مبادئ تصميم القيود
| المبدأ | الوصف |
|---|---|
| الحد الأدنى من الامتيازات | طبق فقط القيود اللازمة؛ لا تُفرط في التقييد |
| التحقق بالقرب من المصدر | حدد على مستوى العمود عندما يكون ذلك ممكنًا، وليس على مستوى الجدول |
| اصطلاح التسمية | أعطِ القيود أسماء ذات معنى لسهولة الصيانة |
| العمل أولاً | يجب أن تعكس القيود قواعد الأعمال، وليس القيود التقنية |
📝 الصيغة الأساسية
-- تحديد القيود عند إنشاء جدول
CREATE TABLE table_name (
column1 datatype PRIMARY KEY,
column2 datatype NOT NULL,
column3 datatype UNIQUE,
column4 datatype CHECK (condition),
column5 datatype DEFAULT value,
column6 datatype,
FOREIGN KEY (column6) REFERENCES other_table(column)
);
-- إضافة قيد إلى جدول موجود
ALTER TABLE table_name ADD CONSTRAINT constraint_name
UNIQUE (column_name);
ALTER TABLE table_name ADD CONSTRAINT constraint_name
CHECK (condition);
-- حذف قيد
ALTER TABLE table_name DROP CONSTRAINT constraint_name;
- يجب أن تكون أسماء القيود فريدة داخل قاعدة البيانات؛ الصيغة الموصى بها:
table_name_column_name_constraint_type NOT NULLيمكن إضافته فقط باستخدامALTER TABLE ... MODIFY، وليسADD CONSTRAINT- إضافة قيد إلى جدول به بيانات موجودة قد يفشل إذا لم تفي البيانات الموجودة بالشرط
📌 أمثلة
مثال: إنشاء جدول موظفين بقيود
CREATE TABLE employees (
employee_id INT PRIMARY KEY,
first_name VARCHAR(50) NOT NULL,
last_name VARCHAR(50) NOT NULL,
email VARCHAR(100) UNIQUE NOT NULL,
phone VARCHAR(20),
hire_date DATE NOT NULL DEFAULT CURRENT_DATE,
salary DECIMAL(10,2) NOT NULL CHECK (salary > 0),
department_id INT,
CONSTRAINT fk_emp_dept FOREIGN KEY (department_id)
REFERENCES departments(department_id)
ON DELETE SET NULL
);
-- إدراج بيانات اختبار
INSERT INTO employees (employee_id, first_name, last_name, email, hire_date, salary, department_id)
VALUES (1, 'John', 'Doe', 'johndoe@company.com', '2026-01-15', 8500.00, 1);
-- البيانات التالية ستفشل:
-- INSERT ... salary = -100 -- ❌ قيد CHECK
-- INSERT ... first_name NULL -- ❌ قيد NOT NULL
-- INSERT ... duplicate email -- ❌ قيد UNIQUE
مثال: إضافة وحذف القيود على جدول موجود
-- إضافة قيد CHECK: الراتب يجب ألا يتجاوز 500000
ALTER TABLE employees ADD CONSTRAINT chk_salary_max
CHECK (salary <= 500000);
-- إضافة قيد UNIQUE: رقم الهاتف يجب أن يكون فريدًا
ALTER TABLE employees ADD CONSTRAINT uq_emp_phone
UNIQUE (phone);
-- حذف قيد
ALTER TABLE employees DROP CONSTRAINT chk_salary_max;
-- إضافة قيد مفتاح خارجي
ALTER TABLE employees ADD CONSTRAINT fk_emp_dept
FOREIGN KEY (department_id) REFERENCES departments(department_id);
🎬 سيناريوهات عملية
السيناريو 1: تصميم قيود نظام طلبات التجارة الإلكترونية
تصميم جدول طلبات لضمان سلامة البيانات.
-- جدول المنتجات
CREATE TABLE products (
product_id INT PRIMARY KEY,
product_name VARCHAR(100) NOT NULL,
price DECIMAL(10,2) NOT NULL CHECK (price > 0),
stock INT NOT NULL DEFAULT 0 CHECK (stock >= 0),
status VARCHAR(20) DEFAULT 'active' CHECK (status IN ('active', 'inactive', 'discontinued'))
);
-- جدول الطلبات
CREATE TABLE orders (
order_id INT PRIMARY KEY,
customer_id INT NOT NULL,
order_date DATE NOT NULL DEFAULT CURRENT_DATE,
total_amount DECIMAL(12,2) CHECK (total_amount > 0),
status VARCHAR(20) DEFAULT 'pending' CHECK (status IN ('pending', 'confirmed', 'shipped', 'cancelled')),
CONSTRAINT fk_order_customer FOREIGN KEY (customer_id)
REFERENCES customers(customer_id)
);
-- جدول عناصر الطلب (مفتاح أساسي مركب)
CREATE TABLE order_items (
order_id INT,
product_id INT,
quantity INT NOT NULL CHECK (quantity > 0),
unit_price DECIMAL(10,2) NOT NULL CHECK (unit_price > 0),
PRIMARY KEY (order_id, product_id),
FOREIGN KEY (order_id) REFERENCES orders(order_id) ON DELETE CASCADE,
FOREIGN KEY (product_id) REFERENCES products(product_id)
);
السيناريو 2: ترحيل قيود نظام إدارة الموظفين
إضافة القيود إلى جدول موجود.
-- الجدول الحالي يفتقر إلى القيود؛ أضفها خطوة بخطوة
-- 1. تأكد من صيغة البريد الإلكتروني
ALTER TABLE employees ADD CONSTRAINT chk_email_format
CHECK (email LIKE '%@%.%');
-- 2. تأكد من معقولية تاريخ التوظيف
ALTER TABLE employees ADD CONSTRAINT chk_hire_date
CHECK (hire_date >= '2000-01-01' AND hire_date <= CURRENT_DATE);
-- 3. تأكد من علاقة المفتاح الخارجي للقسم
ALTER TABLE employees ADD CONSTRAINT fk_emp_dept
FOREIGN KEY (department_id) REFERENCES departments(department_id)
ON DELETE SET NULL;
-- 4. عرض جميع القيود على جدول (SQL Server)
SELECT name, type_desc
FROM sys.objects
WHERE parent_object_id = OBJECT_ID('employees')
AND type IN ('C', 'F', 'UQ', 'PK');
❓ أسئلة شائعة
س: هل يمكن أن يحتوي الجدول على عدة قيود UNIQUE؟ ج: نعم. يمكن أن يحتوي الجدول على
PRIMARY KEYواحد فقط، لكن يمكن أن يحتوي على عدة قيودUNIQUE. على سبيل المثال، كلا من البريد الإلكتروني ورقم الهاتف يمكن أن يكون لكل منهما قيدUNIQUE.
س: هل المفاتيح الخارجية تُقلل الأداء؟ ج: المفاتيح الخارجية تتطلب عمليات تحقق إضافية أثناء الكتابة، مما يؤثر قليلاً على أداء الكتابة. لكن لمعظم التطبيقات، سلامة البيانات أهم من الفارق البسيط في الأداء. لسيناريوهات الكتابة العالية، فكر في التحقق على مستوى التطبيق.
س: هل قيد CHECK يدعم الاستعلامات الفرعية؟ ج: معظم قيود
CHECKفي قواعد البيانات لا تدعم الاستعلامات الفرعية. إذا كنت تحتاج إلى التحقق عبر الجداول، استخدم المشغلات أو قم بالتنفيذ على مستوى التطبيق.
س: ما العلاقة بين DEFAULT وNOT NULL؟ ج: إذا تم تعيين
NOT NULLبدونDEAULT، يجب عليك تقديم قيمة صراحة عند الإدراج. إذا تم تعيينDEFAULT، يمكنك تخطي العمود أثناء الإدراج وتُستخدم القيمة الافتراضية تلقائيًا. غالبًا ما يُستخدمان معًا.
📖 ملخص
| القيد | الغرض | يسمح بـ NULL | يسمح بعدة |
|---|---|---|---|
| PRIMARY KEY | مُعرّف فريد للصف | ❌ | 1 لكل جدول |
| FOREIGN KEY | يشير إلى مفتاح أساسي في جدول آخر | ✅ | ✅ |
| UNIQUE | فريدية قيمة العمود | ✅ | ✅ |
| NOT NULL | العمود لا يمكن أن يكون فارغًا | ❌ | ✅ |
| CHECK | قيمة العمود تفي بالشرط | يعتمد على التعريف | ✅ |
| DEFAULT | القيمة الافتراضية عند عدم التحديد | - | 1 لكل عمود |
- القيود هي "خط الدفاع الأول" لقاعدة البيانات، تتحقق تلقائيًا من البيانات أثناء الكتابة
- فضّل تحديد القيود على مستوى قاعدة البيانات بدلاً من الاعتماد فقط على مستوى التطبيق
- اسمي قيودك لسهولة الصيانة وتصحيح الأخطاء
📝 تمارين
- أنشئ جدول
customersيحتوي على: مفتاح أساسي، اسم غير فارغ، بريد إلكتروني فريد، هاتف اختياري، تاريخ التسجيل (الافتراضي اليوم)، وعمر (18-120). - أنشئ جدول
ordersبمفتاح خارجي يشير إلىcustomers، مبلغ الطلب يجب أن يكون أكبر من 0، والحالة يمكن أن تكون فقطpendingأوpaidأوshippedأوcompleted. - أضف قيد
CHECKإلى جدولemployeesالموجود لضمان أن الراتب لا يقل عن الحد الأدنى للأجور 2000. - فكر: إذا حذفت قسمًا من جدول
departmentsبينما لا يزال موظفون في جدولemployeesينتمون إلى ذلك القسم، ماذا يحدث مع استراتيجياتON DELETEالمختلفة؟
الدرس التالي
سنطبق بعد ذلك كل شيء من خلال تدريب: استعلامات متعددة الجداول الشاملة — دمج JOINs والاستعلامات الفرعية وعمليات المجموعات لحل سيناريوهات أعمال حقيقية.



