القيود والمفاتيح

القيود والمفاتيح

🌍 تشبيه من العالم الحقيقي

فكر في قاعدة البيانات كمكتب صارم:

القيود هي "حراس القواعد" في قاعدة البيانات — تتحقق تلقائيًا من البيانات أثناء الكتابة لمنع دخول البيانات القذرة.


🎯 المفاهيم الأساسية

PRIMARY KEY

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

SQL
CREATE TABLE employees (
    employee_id INT PRIMARY KEY,
    first_name VARCHAR(50) NOT NULL
);

يمكن أن يتكون المفتاح الأساسي أيضًا من عدة أعمدة (مفتاح أساسي مركب):

SQL
CREATE TABLE order_items (
    order_id INT,
    product_id INT,
    quantity INT,
    PRIMARY KEY (order_id, product_id)
);

FOREIGN KEY

يضمن أن القيم في جدول واحد يجب أن توجد في جدول آخر، مما يحافظ على سلامة المراجع.

SQL
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
SQL
FOREIGN KEY (department_id) REFERENCES departments(department_id)
    ON DELETE SET NULL
    ON UPDATE CASCADE;

قيد UNIQUE

يضمن أن جميع القيم في العمود غير مكررة، لكنه يسمح بـ NULL (في معظم قواعد البيانات، يُسمح بعدة قيم NULL).

SQL
CREATE TABLE employees (
    employee_id INT PRIMARY KEY,
    email VARCHAR(100) UNIQUE
);

الاختلافات بين UNIQUE و PRIMARY KEY:

الميزة PRIMARY KEY UNIQUE
قيم NULL ❌ غير مسموح ✅ مسموح
العدد واحد فقط لكل جدول يمكن أن يكون عدة
الغرض مُعرّف فريد للصف فريدية قيمة العمود

قيد NOT NULL

يضمن أن العمود لا يمكن أن يخزن قيم NULL.

SQL
CREATE TABLE employees (
    employee_id INT PRIMARY KEY,
    first_name VARCHAR(50) NOT NULL,
    last_name VARCHAR(50) NOT NULL
);

قيد CHECK

يضمن أن القيم في العمود تفي بشرط محدد.

SQL
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)

عند إدراج بيانات بدون تحديد قيمة للعمود، يتم استخدام القيمة الافتراضية تلقائيًا.

SQL
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
);

مبادئ تصميم القيود

المبدأ الوصف
الحد الأدنى من الامتيازات طبق فقط القيود اللازمة؛ لا تُفرط في التقييد
التحقق بالقرب من المصدر حدد على مستوى العمود عندما يكون ذلك ممكنًا، وليس على مستوى الجدول
اصطلاح التسمية أعطِ القيود أسماء ذات معنى لسهولة الصيانة
العمل أولاً يجب أن تعكس القيود قواعد الأعمال، وليس القيود التقنية

📝 الصيغة الأساسية

SQL
-- تحديد القيود عند إنشاء جدول
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
  • إضافة قيد إلى جدول به بيانات موجودة قد يفشل إذا لم تفي البيانات الموجودة بالشرط

📌 أمثلة

مثال: إنشاء جدول موظفين بقيود

SQL
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
▶ جرّب الكود

مثال: إضافة وحذف القيود على جدول موجود

SQL
-- إضافة قيد 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: تصميم قيود نظام طلبات التجارة الإلكترونية

تصميم جدول طلبات لضمان سلامة البيانات.

SQL
-- جدول المنتجات
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: ترحيل قيود نظام إدارة الموظفين

إضافة القيود إلى جدول موجود.

SQL
-- الجدول الحالي يفتقر إلى القيود؛ أضفها خطوة بخطوة

-- 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 لكل عمود

📝 تمارين

  1. أنشئ جدول customers يحتوي على: مفتاح أساسي، اسم غير فارغ، بريد إلكتروني فريد، هاتف اختياري، تاريخ التسجيل (الافتراضي اليوم)، وعمر (18-120).
  2. أنشئ جدول orders بمفتاح خارجي يشير إلى customers، مبلغ الطلب يجب أن يكون أكبر من 0، والحالة يمكن أن تكون فقط pending أو paid أو shipped أو completed.
  3. أضف قيد CHECK إلى جدول employees الموجود لضمان أن الراتب لا يقل عن الحد الأدنى للأجور 2000.
  4. فكر: إذا حذفت قسمًا من جدول departments بينما لا يزال موظفون في جدول employees ينتمون إلى ذلك القسم، ماذا يحدث مع استراتيجيات ON DELETE المختلفة؟

الدرس التالي

سنطبق بعد ذلك كل شيء من خلال تدريب: استعلامات متعددة الجداول الشاملة — دمج JOINs والاستعلامات الفرعية وعمليات المجموعات لحل سيناريوهات أعمال حقيقية.

Web-Tutorial.com

فريق Web-Tutorial التقني

منصة دروس برمجية يديرها عدة مطورين. كل درس يتم كتابته ومراجعته بواسطة مطورين متخصصين في المجال. نعمل على ضمان دقة وموثوقية المحتوى — إذا لاحظت أي مشكلة، فيرجى إخبارنا.

100%