القيود (Constraints) في SQL هي قواعد تُفرَض على أعمدة الجدول لضمان دقة البيانات وسلامتها ومنع إدخال قيم غير منطقية منذ اللحظة الأولى. وبدلاً من الاعتماد على كود التطبيق وحده للتحقق من صحة البيانات، تنقل القيود جزءاً من هذه المسؤولية إلى قاعدة البيانات نفسها، فتصبح البيانات محميّة مهما كان مصدر الإدخال. تُعرَّف القيود عادةً عند إنشاء الجدول داخل أمر CREATE TABLE، كما يمكن إضافتها لاحقاً باستخدام ALTER TABLE.
لماذا نستخدم القيود؟
تخيّل جدولاً لتسجيل المستخدمين بلا أي قيود: قد يُدخِل أحدهم مستخدماً بلا اسم، أو يكرّر البريد الإلكتروني نفسه مرتين، أو يضع عمراً سالباً. كل هذه الحالات تُفسد جودة البيانات وتسبب أخطاءً يصعب تتبّعها لاحقاً. القيود تمنع هذه المشكلات من جذورها، فترفض قاعدة البيانات أي عملية إدخال أو تعديل تخالف القاعدة المحددة، وتُرجع رسالة خطأ واضحة تشرح سبب الرفض.
قيد NOT NULL
يفرض قيد NOT NULL وجود قيمة في العمود، فلا يُسمح بترك الخانة فارغة (NULL). يُستخدم مع الأعمدة التي لا معنى لها بدون قيمة، مثل اسم المستخدم أو بريده الإلكتروني.
name VARCHAR(100) NOT NULL
إذا حاولت إدراج صف دون تحديد قيمة لهذا العمود، سترفض قاعدة البيانات العملية وتُظهر خطأً يفيد بأن العمود لا يقبل القيمة الفارغة.
قيد UNIQUE
يضمن قيد UNIQUE ألا تتكرر القيمة في العمود عبر الصفوف المختلفة، وهو مناسب تماماً للحقول التي يجب أن تكون مميزة لكل مستخدم مثل البريد الإلكتروني أو رقم الهاتف. ويمكن أن يحتوي الجدول الواحد على أكثر من عمود UNIQUE، بعكس المفتاح الأساسي الذي يكون واحداً فقط.
email VARCHAR(150) UNIQUE
قيد PRIMARY KEY
المفتاح الأساسي (PRIMARY KEY) يُعرّف كل صف بشكل فريد داخل الجدول، وهو يجمع بين خاصيتَي NOT NULL وUNIQUE في آنٍ واحد: لا يقبل قيمة فارغة ولا يقبل التكرار. ولكل جدول مفتاح أساسي واحد فقط، وغالباً ما يكون عموداً رقمياً يزداد تلقائياً.
id INT PRIMARY KEY AUTO_INCREMENT
تجعل الكلمة AUTO_INCREMENT قاعدةَ البيانات تولّد رقماً جديداً تلقائياً لكل صف يُضاف، فلا حاجة لإدخاله يدوياً.
قيد FOREIGN KEY
المفتاح الأجنبي (FOREIGN KEY) يربط بين جدولين عبر عمود مشترك، ويضمن ما يُعرف بسلامة المرجعية (Referential Integrity): لا يمكن إدخال قيمة في العمود المرتبط إلا إذا كانت موجودة فعلاً في الجدول المرجعي. وبهذا نمنع وجود طلب منسوب إلى مستخدم غير موجود أصلاً.
CREATE TABLE orders (
id INT PRIMARY KEY,
user_id INT,
FOREIGN KEY (user_id) REFERENCES users(id)
);
في المثال أعلاه يجب أن يطابق عمود user_id في جدول orders قيمةَ id موجودةً في جدول users، وإلا رفضت قاعدة البيانات الإدخال.
قيد DEFAULT
يحدد قيد DEFAULT قيمةً افتراضية تُستخدم تلقائياً عند عدم تحديد قيمة للعمود أثناء الإدراج، ما يوفّر كتابة القيم المتكررة في كل مرة.
status VARCHAR(20) DEFAULT 'نشط'
قيد CHECK
يفرض قيد CHECK شرطاً منطقياً يجب أن تحققه القيمة قبل قبولها، فيمنع مثلاً إدخال عمر سالب أو أكبر من حدٍّ معقول.
age INT CHECK (age >= 0 AND age <= 120)
مثال عملي شامل
يجمع الكود التالي كل القيود السابقة في تعريف جدول واحد:
CREATE TABLE users (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(100) NOT NULL,
email VARCHAR(150) UNIQUE NOT NULL,
age INT CHECK (age >= 0 AND age <= 120),
status VARCHAR(20) DEFAULT 'نشط'
);
بهذا التعريف، أي محاولة لإضافة مستخدم بلا اسم، أو ببريد مكرّر، أو بعمر سالب، سترفضها قاعدة البيانات تلقائياً، بينما يُملأ حقل status بقيمة “نشط” إذا لم يُحدَّد صراحةً.
أخطاء شائعة
- نسيان قيد NOT NULL على أعمدة أساسية، ما يسمح بحفظ صفوف ناقصة البيانات.
- استخدام UNIQUE على عمود يُفترض تكراره (كاسم المدينة)، ما يسبب أخطاءً غير متوقعة عند الإدخال.
- إضافة FOREIGN KEY قبل إنشاء الجدول المرجعي، فيفشل تنفيذ الأمر.
- الاعتماد على كود التطبيق وحده في التحقق وإهمال القيود في قاعدة البيانات.
تمرين محلول
المطلوب: صمّم جدول products بحيث يكون لكل منتج معرّف فريد تلقائي، واسم إجباري، وسعر لا يقل عن صفر، وحالة توفّر افتراضية قيمتها “متاح”.
الحل:
CREATE TABLE products (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(150) NOT NULL,
price DECIMAL(10,2) CHECK (price >= 0),
availability VARCHAR(20) DEFAULT 'متاح'
);
لاحظ كيف عبّر كل قيد عن قاعدة عمل واضحة: المعرّف فريد، والاسم إجباري، والسعر غير سالب، والحالة لها قيمة افتراضية تُغني عن إدخالها يدوياً.
الخلاصة
القيود هي خط الدفاع الأول عن جودة البيانات داخل قاعدة البيانات. وإتقان NOT NULL وUNIQUE وPRIMARY KEY وFOREIGN KEY وDEFAULT وCHECK يمنحك القدرة على تصميم جداول قوية تحمي نفسها من البيانات الخاطئة، ويقلّل بشكل كبير من الأخطاء في طبقة التطبيق.
