ما هى أنواع الـ SQL Constraints المختلفة
السلام عليكم ورحمة الله وبركاته
المقدمة
في المقالة السابقة تعلمنا كيفية إنشاء قواعد البيانات والجداول باستخدام أمر CREATE
ولاحظنا أثناء إنشاء الجداول أننا استخدمنا بعض الكلمات مثل NOT NULL
و PRIMARY KEY
و DEFAULT
هذه الكلمات تسمى Constraints
في الـ SQL
وهي قواعد وشروط نضعها على الجداول والأعمدة لضمان سلامة البيانات
في هذه المقالة سنتعلم كل شيء عن هذه القيود وكيفية استخدامها
ما هي قيود الـ SQL ؟
الـ SQL Constraints
هي قواعد وشروط نطبقها على البيانات في الجداول
الغرض منها هو التأكد من صحة البيانات وسلامتها وهى كما يوحي اسمها قيود على البيانات
تخيل معي أنك تقوم بإنشاء تطبيق لإدارة حسابات المستخدمين
هنا ستحتاج إلى التأكد من أن:
- كل مستخدم له بريد إلكتروني لا يتكرر
- لا يمكن إنشاء حساب بدون اسم مستخدم
- عمر المستخدم يجب أن يكون أكبر من
13
سنة مثلًا - إذا لم يحدد المستخدم الدولة، فستكون القيمة الافتراضية "غير محدد"
كل هذه الأمور يمكننا تطبيقها على الجدول باستخدام الـ SQL Constraints
أنواع قيود الـ SQL
هناك عدة أنواع من القيود في الـ SQL
، وسنتعلم كل نوع بالتفصيل:
NOT NULL
: يمنع أن يكون العمود فارغًاUNIQUE
: يضمن أن كل القيم في العمود مختلفة ولا تتكررPRIMARY KEY
: هو مثل رقم الهوية الخاص بكل صف في الجدول، ويجمع بينNOT NULL
وUNIQUE
FOREIGN KEY
: يربط بين جدولينCHECK
: يتحقق من شرط معينDEFAULT
: يضع قيمة افتراضية في العمود إذا لم يتم تحديد قيمة
دعونا نتعلم كل واحد منها بالتفصيل
الـ NOT NULL
في حالة أنك أنشأت عمودًا في جدول ولم تحدد له قيمة، فإن قاعدة البيانات ستضع له قيمة NULL
وهذا يجعل العمود يقبل أن يكون فارغًا وبالطبع هذا الأمر ليس جيدًا في بعض الحالات
لأنك دائمًا ما ستحتاج إلى التأكد من أن بعض الأعمدة تحتوي على قيم ولا تريدها أن تكون فارغة
لذلك في هذه الحالة نستخدم Constraint
الـ NOT NULL
يعني أن العمود لا يمكن أن يكون فارغًا أبدًا
وإذا حاول أحد إدخال صف بدون قيمة في هذا العمود، ستظهر له رسالة خطأ
لنرى مثالًا على ذلك
CREATE TABLE Students (
id INT NOT NULL,
name VARCHAR(100) NOT NULL,
email VARCHAR(100) NOT NULL,
age INT,
level VARCHAR(50)
);
في هذا المثال، جعلنا الأعمدة id
و name
و email
لا تقبل NULL
بينما age
و level
يمكن أن يكونا فارغين
ويمكنك التأكد من ذلك باستخدام الأمر DESCRIBE Students;
+-------+------------------+-------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------+------------------+-------+-----+---------+----------------+
| id | int | NO | | NULL | |
| name | varchar(100) | NO | | NULL | |
| email | varchar(100) | NO | | NULL | |
| age | int | YES | | NULL | |
| level | varchar(50) | YES | | NULL | |
+-------+------------------+-------+-----+---------+----------------+
كما ترى العمود name
و email
و id
لا يقبلون NULL
بينما العمود age
و level
يقبلان NULL
بالتالي في حالة محاولة إدخال طالب جديد بدون اسم باستخدام الأمر INSERT
:
INSERT INTO Students (id, email, age)
VALUES (1, '[email protected]', 20);
لاحظ أننا هنا نضيف طالب جديد بدون name
وتذكر أننا جعلنا name
لا يقبل NULL
ولا يملك قيمة افتراضية
بالتالي إن حاولنا تنفيذ هذا الأمر، ستظهر رسالة خطأ مثل:
SQL Error: Field 'name' doesn't have a default value
أعلم أننا لم نشرح الأمر INSERT
بعد، لكن لا تقلق، سنغطيه في المقالة القادمة
لكن كل ما عليك أن تعرفه الآن هو أن هذا الأمر INSERT
يستخدم لإضافة بيانات جديدة إلى الجدول
وهنا نحن حاولنا إضافة صف واحد في جدول Students
بالبيانات التالية بالترتيب بحيث أن id
هو 1
و email
هو '[email protected]'
و age
هو 20
لكن الفكرة هنا هي أن الـ Constraint
المسمى NOT NULL
الذي وضعناه على العمود name
يمنعنا من إدخال صف بدون قيمة في هذا العمود
بالتالي لأننا لم نعطي قيمة للعمود name
، فتظهر لنا رسالة تخبرنا أن العمود name
لا يملك قيمة افتراضية
إذا كان لديك جدول موجود بالفعل وتريد إضافة NOT NULL
لعمود معين
فنحن نستطيع فعل ذلك باستخدام الأمر ALTER TABLE
مع MODIFY
أو ALTER COLUMN
حسب نوع الـ DBMS
التي تستخدمها
ALTER TABLE Students
MODIFY name VARCHAR(100) NOT NULL;
ملحوظة
: لا يمكن إضافةConstraint
الـNOT NULL
في عمود معين في حالة وجود بيانات في الجدول تحتوي على قيمNULL
في هذا العمود
في هذه الحالة، يجب عليك أولًا تحديث هذه الصفوف للتأكد من عدم وجود أي صفوف تحتوي علىNULL
في هذا العمود قبل إضافة الـConstraint
الـ UNIQUE
أحيانًا تحتاج إلى التأكد من أن القيم في عمود معين لا تتكرر أبدًا
مثلًا، لا يمكن أن يكون هناك طالبان لهما نفس البريد الإلكتروني أو نفس رقم الطالب
هنا يأتي دور قيد UNIQUE
الذي يضمن أن كل القيم في العمود مختلفة
فإذا حاول أحد إدخال قيمة موجودة بالفعل، ستظهر رسالة خطأ
لنتخيل أننا نريد إنشاء جدول للطلاب، ونريد أن يكون لكل طالب بريد إلكتروني مختلف أي لا يمكن تكراره
CREATE TABLE Students (
id INT NOT NULL,
name VARCHAR(100) NOT NULL,
email VARCHAR(100) UNIQUE,
age INT,
level VARCHAR(50)
);
في هذا المثال، جعلنا email
يكون UNIQUE
بمعنى أنه لا يمكن أن يكون هناك طالبان لهما نفس البريد الإلكتروني
لنرى ماذا سيقول لنا أمر الـ DESCRIBE Students;
بعد إنشاء الجدول:
+-------+------------------+-------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------+------------------+-------+-----+---------+----------------+
| id | int | NO | | NULL | |
| name | varchar(100) | NO | | NULL | |
| email | varchar(100) | YES | UNI | NULL | |
| age | int | YES | | NULL | |
| level | varchar(50) | YES | | NULL | |
+-------+------------------+-------+-----+---------+----------------+
هنا العمود email
يحمل علامة UNI
، مما يعني أنه UNIQUE
ماذا يحدث عند محاولة التكرار ؟
لنفترض أننا أدخلنا طالبين، والثاني له نفس البريد الإلكتروني:
INSERT INTO Students (id, name, email, age, level)
VALUES (1, 'Ahmed Moustafa', '[email protected]', 20, 'Beginner');
INSERT INTO Students (id, name, email, age, level)
VALUES (2, 'Ahmed Ali', '[email protected]', 22, 'Intermediate');
في هذه الحالة الطالب الأول سينجح في التسجيل بنجاح لأنه لا يوجد طالب آخر بنفس البريد الإلكتروني
ثم عندما نحاول تسجيل الطالب الثاني بنفس البريد الإلكتروني [email protected]
، ستظهر رسالة خطأ
SQL ERROR: Duplicate entry '[email protected]' for key 'Students.email'
وهذا بالضبط ما نريده، لأننا لا نريد طالبين لهما نفس البريد الإلكتروني
يمكنك كتابة الـ Constraint
الخاص بالـ UNIQUE
عند إنشاء الجدول باستخدام CREATE TABLE
بطريقة أخرى:
CREATE TABLE Students (
id INT NOT NULL,
name VARCHAR(100) NOT NULL,
email VARCHAR(100),
age INT,
level VARCHAR(50),
CONSTRAINT unique_email UNIQUE (email)
);
هنا استخدمنا CONSTRAINT unique_email UNIQUE (email)
لتحديد العمود email
كـ UNIQUE
وهذه طريقة تدعمها معظم الـ DBMS
وأيضًا يوجد طريقة أبسط تستخدمها في بعض الـ DBMS
مثل MySQL
:
CREATE TABLE Students (
id INT NOT NULL,
name VARCHAR(100) NOT NULL,
email VARCHAR(100),
age INT,
level VARCHAR(50),
UNIQUE (email)
);
لاحظ أننا لم نستخدم CONSTRAINT
هنا، بل فقط كتبنا UNIQUE (email)
بشكل مباشر
وهذه الطريقة تعمل في معظم أنظمة إدارة قواعد البيانات مثل MySQL
سؤال، الآن نحن قمنا بجعل الـ email
يكون UNIQUE
فهل هذا يعني أنه لا يقبل NULL
؟
لوهلة الأولى قد تقول لي بالطبع لا يقبل NULL
لكن إن فكرت في الأمر قليلًا، ستجد العمود الذي عليه UNIQUE
يمكن أن يحتوي على قيم NULL
لكن يمكن أن يكون هناك صف واحد فقط يحتوي على NULL
في هذا العمود
CREATE TABLE Students (
id INT NOT NULL,
name VARCHAR(100) NOT NULL,
email VARCHAR(100) UNIQUE, -- NULL يمكن أن يقبل
age INT,
level VARCHAR(50)
);
هنا العمود email
يمكن أن يحتوي على قيمة NULL
بمعنى أنه يمكن أن يكون لدينا طالب واحد تكون قيمة الـ email
عنده بـ NULL
كأن قيمة الـ NULL
هي قيمة بحد ذاتها بالتالي فأن الـ UNIQUE
سيتعامل معها كأنها قيمة مستقلة مثل أي قيمة أخرى
وإذا أردت أن تجعل العمود email
يكون NOT NULL
وأيضًا UNIQUE
في نفس الوقت، يمكنك فعل ذلك عند إنشاء الجدول:
CREATE TABLE Students (
id INT NOT NULL,
name VARCHAR(100) NOT NULL,
email VARCHAR(100) NOT NULL UNIQUE, -- NULL لا يقبل
age INT,
level VARCHAR(50)
);
الآن العمود email
لا يقبل NULL
لأننا جعلناه NOT NULL
وأيضًا لا يمكن أن يتكرر بسبب الـ UNIQUE
إذا كان لديك جدول موجود بالفعل وتريد إضافة UNIQUE
لعمود معين
فنحن نستخدم الأمر ALTER TABLE
لتعديل أي Constraint
على الجدول
سواء حذف أو إضافة Constraint
ALTER TABLE Students
ADD CONSTRAINT unique_email UNIQUE (email);
هنا في الـ ALTER TABLE
نحدد اسم الجدول Users
ثم نكتب الأمر ADD CONSTRAINT
ثم نكتب اسم الـ Constraint
الذي نريد إضافته، مثلًا هنا كتبنا unique_email
وهذا اسم اختياري خاص باسم الـ Constraint
ثم نكتب UNIQUE (email)
لتحديد العمود الذي نريد تطبيق الـ Constraint
عليه
هكذا نكون قد أضفنا Constraint
الـ UNIQUE
على العمود email
في جدول Users
واسم الـ Constraint
هو unique_email
، ويمكننا استخدامه لاحقًا إذا أردنا حذف أو تعديل هذا الـ Constraint
وهو اسم اختياري، يمكنك عدم كتابته وسيتم توليد اسم تلقائي للـ Constraint
ALTER TABLE Users
ADD CONSTRAINT UNIQUE (username);
هنا كتبنا ADD CONSTRAINT UNIQUE (username)
دون تحديد اسم للـ Constraint
وبالتالي سيقوم نظام الـ DBMS
الذي نستخدمه أيًا ما كان بتوليد اسم تلقائي Constraint
مثل ck_users_username_1234
أو unique_username
وأيضًا في بعض أنظمة الـ DBMS
يمكننا الاستغناء عن كلمة CONSTRAINT
ALTER TABLE Users
ADD UNIQUE (username);
هذه طرق مختلفة لإضافة UNIQUE
إلى عمود في جدول موجود
والاسم كما قلنا مفيد في حالة إذا أردنا حذف الـ Constraint
لاحقًا أو تعديله
فعلى سبيل المثال لو أردنا حذف Constraint
الـ UNIQUE
الذي أضفناه، يمكننا فعل ذلك باستخدام الأمر ALTER TABLE
ALTER TABLE Users
DROP CONSTRAINT unique_username;
هنا يجب ان نكتب اسم الـ Constraint
الذي أضفناه سابقًا، وهو unique_username
وتذكر في حالة إذا أردت إضافة Constraint
الـ UNIQUE
فأنه لن ينجح إذا كان هناك قيم مكررة في العمود
بالتالي يجب عليك حذف أو تعديل القيم المكررة أولًا والتأكد من عدم وجود أي قيم مكررة في العمود ثم يمكنك إضافة Constraint
الـ UNIQUE
إلى العمود
الـ PRIMARY KEY
الـ PRIMARY KEY
هو واحد من أهم المفاهيم في قواعد البيانات
يمكنك تخيله كـ "رقم الهوية" لكل صف في الجدول، بحيث لا يمكن أن يتكرر أبدًا
والـ PRIMARY KEY
في الواقع هو مزيج بين الـ NOT NULL
والـ UNIQUE
PRIMARY KEY = NOT NULL + UNIQUE
بمعنى أنه لا يمكن أن يكون فارغًا ولا يمكن أن يتكرر
ملحوظة
: كل جدول لهPRIMARY KEY
واحد فقط، ولا يمكن أن يكون هناك أكثر من عمودPRIMARY KEY
في نفس الجدول
لكن يمكن أن يملك الجدول أكثر من عمودUNIQUE
تخيل أن لديك جدول للطلاب، وهناك طالبان اسمهما "أحمد محمد محمود حمادة" وعمرهما 20
سنة ومن نفس المدينة
كيف ستميز بينهما؟ هنا يأتي دور الـ PRIMARY KEY
في الغالب نحن نستخدم عمودًا يسمى id
ليكون الـ PRIMARY KEY
، وهو رقم مختلف لكل طالب
بالتالي حتى لو كان هناك طالبان بنفس الاسم، يمكنك تمييزهما باستخدام id
CREATE TABLE Students (
id INT PRIMARY KEY,
name VARCHAR(100) NOT NULL,
email VARCHAR(100) NOT NULL UNIQUE,
age INT,
level VARCHAR(50)
);
هنا العمود id
هو الـ PRIMARY KEY
، وهو ما يمثل "رقم الهوية" لكل طالب
وعندما ننظر إلى DESCRIBE Students;
، سنرى أن العمود id
يحمل علامة PRI
، مما يعني أنه PRIMARY KEY
+-------+------------------+-------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------+------------------+-------+-----+---------+----------------+
| id | int | NO | PRI | NULL | |
| name | varchar(100) | NO | | NULL | |
| email | varchar(100) | NO | UNI | NULL | |
| age | int | YES | | NULL | |
| level | varchar(100) | YES | | NULL | |
+-------+------------------+-------+-----+---------+----------------+
تذكر أن PRIMARY KEY
هو مزيج من NOT NULL
و UNIQUE
لذا سيضمن لنا هنا أن كل طالب لديه id
مختلف ولا يمكن أن يكون فارغًا
ويمكنك أيضًا تحديد الـ PRIMARY KEY
عند إنشاء الجدول باستخدام CREATE TABLE
طريقة أخرى:
CREATE TABLE Students (
id INT,
name VARCHAR(100) NOT NULL,
email VARCHAR(100) NOT NULL UNIQUE,
age INT,
level VARCHAR(50),
CONSTRAINT pk_student PRIMARY KEY (id)
);
هنا استخدمنا CONSTRAINT pk_student PRIMARY KEY (id)
لتحديد العمود id
كـ PRIMARY KEY
مثل ما فعلنا مع الـ UNIQUE
أو يمكنك ببساطة كتابة:
CREATE TABLE Students (
id INT,
name VARCHAR(100) NOT NULL,
email VARCHAR(100) NOT NULL UNIQUE,
age INT,
level VARCHAR(50),
PRIMARY KEY (id)
);
هنا استخدمنا PRIMARY KEY (id)
لتحديد العمود id
كـ PRIMARY KEY
وهذه الطريقة تدعمها بعض أنظمة الـ DBMS
مثل MySQL
وأيضًا إذا كان لديك جدول موجود وتريد إضافة PRIMARY KEY
لعمود معين، يمكنك فعل ذلك باستخدام الأمر ALTER TABLE
:
ALTER TABLE Students
ADD CONSTRAINT pk_student PRIMARY KEY (id);
في هذه الحالة عليك التأكد من أن العمود id
لا يحتوي على قيم NULL
أو أن يكون يملك CONSTRAINT
الـ NOT NULL
على أي حال في بعض الـ DBMS
مثل MySQL
، يتم تجاهل الاسم الذي تعطيه للـ PRIMARY KEY
لأنه يتم التعامل مع الـ PRIMARY KEY
كحالة مميزة عن باقي الـ Constraints
بالتالي لا داعي لتحديد اسم للـ PRIMARY KEY
، يمكنك ببساطة كتابة:
ALTER TABLE Students
ADD PRIMARY KEY (id);
ولو أردت حذف الـ PRIMARY KEY
، يمكنك فعل ذلك باستخدام الأمر ALTER TABLE
:
ALTER TABLE Students
DROP PRIMARY KEY;
لذا عليك الانتباه لهذه الفروقات عندما تتعامل مع أنظمة الـ DBMS
المختلفة مثل MySQL
أو PostgreSQL
أو SQL Server
الـ Composite Primary Key
الـ Composite Primary Key
هو نوع من الـ PRIMARY KEY
يتكون من أكثر من عمود
بمعنى أنه مجرد PRIMARY KEY
لكن قيمته يستمدها من أكثر من عمود
بمعنى تخيل أنك لديك جدول لتسجيل الطلاب في الدورات، حيث يمكن لكل طالب أن يسجل في أكثر من دورة
بالتالي سيكون لدينا جدول الـ Students
و جدول الـ Courses
ونحتاج لجدول ثالث يربط بين الطلاب والدورات، وهو جدول الـ StudentCourses
أو يمكنك تسميته Enrollments
شكل هذا الجدول سيكون كالتالي:
CREATE TABLE StudentCourses (
student_id INT,
course_id INT,
enrollment_date DATE,
PRIMARY KEY (student_id, course_id)
);
لاحظ هنا أن جدول StudentCourses
يستخدم الـ PRIMARY KEY
مركب من student_id
و course_id
وهذا هو الـ Composite Primary Key
في هذه الحالة الـ PRIMARY KEY
هو مزيج من student_id
و course_id
بالتالي كل زوج من student_id
و course_id
يجب أن يكونا UNIQUE
ولا يتكررا
بالتالي، فهذا يعني أن الطالب الواحد لا يمكن أن يسجل في نفس الدورة مرتين
دعونا نلقي نظرة على الـ DESCRIBE StudentCourses;
ونرى كيف يبدو:
+----------------+------------------+-------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+----------------+------------------+-------+-----+---------+----------------+
| student_id | int | NO | PRI | NULL | |
| course_id | int | NO | PRI | NULL | |
| enrollment_date| date | YES | | NULL | |
+----------------+------------------+-------+-----+---------+----------------+
هنا قد تظن أننا نلك اثنين من الـ PRIMARY KEY
، وهذا ليس صحيحًا
لأننا لدينا Composite Primary Key
يتكون من عمودين هما student_id
و course_id
وليس لدينا PRIMARY KEY
منفصل لكل عمود
تذكر أن أي جدول لا يمكن أن يحتوي على أكثر من PRIMARY KEY
واحد
ولو أردت إثبات صحة ما أقول يمكنك تجربة الأمر التالي:
SELECT *
FROM information_schema.TABLE_CONSTRAINTS
WHERE TABLE_NAME = 'StudentCourses';
أعلم أننا لم نشرح الـ SELECT
بعد لكن أظنك بمجرد النظر إلى هذا الأمر ستفهم أنه يحضر لنا كل شيء متعلق بالـ CONSTRAINTS
الخاصة بجدول StudentCourses
من جدول أخر يدعى information_schema.TABLE_CONSTRAINTS
ومعظم أنظمة الـ DBMS
تخزن معلومات داخلية لها في جداول خاصة بها داخل Database
مثل information_schema
على أي حال، بعد تنفيذ هذا الأمر ستجد جدولًا يحتوي على كل الـ CONSTRAINTS
الخاصة بجدول StudentCourses
والذي سيظهر لك أن هناك PRIMARY KEY
واحد فقط وهو Composite Primary Key
وليس اثنين
+--------------------+-------------------+-----------------+-----------------+-----------------+-----------------+----------+
| CONSTRAINT_CATALOG | CONSTRAINT_SCHEMA | CONSTRAINT_NAME | TABLE_SCHEMA | TABLE_NAME | CONSTRAINT_TYPE | ENFORCED |
+--------------------+-------------------+-----------------+-----------------+-----------------+-----------------+----------+
| def | school | PRIMARY | school | studentcourses | PRIMARY KEY | YES |
+--------------------+-------------------+-----------------+-----------------+-----------------+-----------------+----------+
ستجده يخبرك أن هناك جدول StudentCourses
يحتوي على PRIMARY KEY
واحد فقط
وبالمناسبة كلمة SCHEMA
المقصود بها هنا هي Database
في بعض أنظمة الـ DBMS
مثل MySQL
لذا ستجد أن TABLE_SCHEMA
هو school
و TABLE_NAME
هو studentcourses
لأننا أنشأنا جدول StudentCourses
داخل قاعدة البيانات school
وتذكر إنشاء قاعدة البيانات school
في مقالة ما هي لغة الـ SQL ؟
بالطبع سيأتي شخص ويقول يمكننا إضافة عمود id
كـ PRIMARY KEY
عادي، لكن هذا ليس منطقيًا في هذه الحالة
ونجعل الـ student_id
و course_id
يكونان UNIQUE
بل يمكننا عمل Composite Unique Key
أيضًا
CREATE TABLE StudentCourses (
id INT PRIMARY KEY,
student_id INT NOT NULL,
course_id INT NOT NULL,
enrollment_date DATE,
UNIQUE (student_id, course_id)
);
هكذا لدينا PRIMARY KEY
منفصل هو id
، وأيضًا لدينا UNIQUE
مركب من student_id
و course_id
وإذا نظرنا إلى المعلومات الموجودة في information_schema.TABLE_CONSTRAINTS
الآن
+--------------------+-------------------+-----------------+-----------------+-----------------+-----------------+----------+
| CONSTRAINT_CATALOG | CONSTRAINT_SCHEMA | CONSTRAINT_NAME | TABLE_SCHEMA | TABLE_NAME | CONSTRAINT_TYPE | ENFORCED |
+--------------------+-------------------+-----------------+-----------------+-----------------+-----------------+----------+
| def | school | PRIMARY | school | studentcourses | PRIMARY KEY | YES |
| def | school | UNIQUE | school | studentcourses | UNIQUE | YES |
+--------------------+-------------------+-----------------+-----------------+-----------------+-----------------+----------+
ستجده أنه أصبح لدينا اثنين من الـ CONSTRAINTS
، أما عندما كان لدينا Composite Primary Key
كان لدينا CONSTRAINT
واحد فقط
بالتالي المثال السابق يستخدم Constraint
اقل لتحقيق نفس الهدف الخاص بالمثال الثاني
بالتالي يستهلك مساحة أقل وسرعة تنفيذ أعلى
لذا من الأفضل استخدام Composite Primary Key
في هذه الحالة وليس استخدام PRIMARY KEY
منفصل مع Composite Unique
وفيما بعد ستعرف أن الـ Constraint
مثل PRIMARY KEY
و UNIQUE
يمكن أن يكون لهما تأثير على الأداء في بعض الحالات
لأنهما ينشئان INDEX
والذي يساعد في تسريع عمليات البحث لكنه يستهلك مساحة أكبر في التخزين
سنتعرف على الـ INDEX
وأنواعها في المقالات القادمة
الـ AUTO_INCREMENT
الـ AUTO_INCREMENT
هو Constraint
يستخدم لجعل العمود يتزايد تلقائيًا كلما أضفنا صفًا جديدًا
بالتالي لو لدينا جدول للطلاب ولدينا عمود يدعى student_number
، يمكننا استخدام AUTO_INCREMENT
لجعل هذا العمود يتزايد تلقائيًا
بالتالي عندما نضيف طالبًا جديدًا، لا نحتاج لتحديد قيمة student_number
، بل سيقوم النظام بتوليدها تلقائيًا
فأول طالب سيأخذ student_number = 1
، والثاني سيأخذ student_number = 2
، وهكذا
هذا يجعلنا نتجنب الأخطاء البشرية في إدخال القيم التي قد تتكرر
ولاحظ أننا عندما تعاملنا مع الـ PRIMARY KEY
في الأمثلة السابقة، كنا نحتاج دائمًا إلى إعطاء قيمة للعمود id
INSERT INTO Students (id, name, email, age, level)
VALUES (1, 'Ahmed Moustafa', '[email protected]', 20, 'Beginner');
لكن هذا يضع مسؤولية على المطور أو المستخدم لضمان أن قيمة الـ id
لا تتكرر
وهذا يفتح المجال للأخطاء البشرية
تخيل أنك تقوم بإدخال طلاب جدد في النظام، وعن طريق الخطأ أعطيت نفس الـ id
لطالبين مختلفين
هنا ستحصل على خطأ لأن الـ PRIMARY KEY
لا يقبل القيم المكررة
وهذا يعني أنك ستحتاج إلى تذكر آخر id
استخدمته في كل مرة تضيف طالب جديد
الحل هو الـ AUTO_INCREMENT
الـ AUTO_INCREMENT
هو خاصية نضعها على عمود من نوع INT
لجعله يتزايد تلقائيًا
بحيث في كل مرة تضيف صف جديد، يقوم النظام تلقائيًا بإعطاء الصف رقم id
مختلف بدون تدخل منك
CREATE TABLE Students (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(100) NOT NULL,
email VARCHAR(100) NOT NULL UNIQUE,
age INT,
level VARCHAR(50)
);
هنا جعلنا الـ id
يكون AUTO_INCREMENT
بمعنى أننا رمينا مسؤولية إنشاء الـ id
للـ Database
بدلًا من أن نتدخل نحن
عندما تضيف الطالب الأول، سيأخذ id = 1
تلقائيًا
عندما تضيف الطالب الثاني، سيأخذ id = 2
تلقائيًا
وهكذا...
لنرى كيف يبدو هذا عند تنفيذ الأمر DESCRIBE Students;
:
+-------+------------------+-------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------+------------------+-------+-----+---------+----------------+
| id | int | NO | PRI | NULL | auto_increment |
| name | varchar(100) | NO | | NULL | |
| email | varchar(100) | NO | UNI | NULL | |
| age | int | YES | | NULL | |
| level | varchar(50) | YES | | NULL | |
+-------+------------------+-------+-----+---------+----------------+
لاحظ أن الـ id
الآن لديه معلومة جديدة في خانة الـ Extra
وهي auto_increment
هذا يعني أن النظام سيتولى إنشاء القيم تلقائيًا
ملحوظة
: الـAUTO_INCREMENT
يعمل فقط مع الأعمدة من نوع الأرقام مثلINT
,BIGINT
، وعادة ما يستخدم مع الـPRIMARY KEY
الـ DEFAULT
أحيانًا تريد أن تضع قيمة افتراضية لعمود معين في حالة عدم تحديد قيمة له عند إدخال البيانات
هنا يأتي دور Constraint
الـ DEFAULT
الذي يسمح لك بتحديد قيمة افتراضية للعمود
في حالتنا لدينا جدول للطلاب وكل طالمة لديه عمود يدعى level
يحدد مستوى الطالب سؤال Beginner
أو Intermediate
أو Advanced
هنا نحتاج لجعل القيمة الافتراضية للعمود level
هي Beginner
في حالة عدم تحديد مستوى الطالب عند إدخال البيانات
يمكننا فعل ذلك باستخدام DEFAULT
:
CREATE TABLE Students (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(100) NOT NULL,
email VARCHAR(100) NOT NULL UNIQUE,
age INT,
level VARCHAR(50) DEFAULT 'Beginner'
);
الآن كما تلاحظ أننا جعلنا العمود level
يحمل قيمة افتراضية هي 'Beginner'
بالتالي إذا حاولنا إدخال طالب جديد بدون تحديد مستوى الطالب، سيأخذ العمود level
القيمة الافتراضية 'Beginner'
لكن إذا حددنا مستوى الطالب، فسيأخذ القيمة التي حددناها
وبالطبع يمكنك رؤية ذلك عند استخدام DESCRIBE Students;
:
+-------------------+------------------+-------+-----+----------------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------------------+------------------+-------+-----+----------------+----------------+
| id | int | NO | PRI | NULL | auto_increment |
| name | varchar(100) | NO | | NULL | |
| email | varchar(100) | NO | UNI | NULL | |
| age | int | YES | | NULL | |
| level | varchar(50) | YES | | Beginner | |
+-------------------+------------------+-------+-----+----------------+----------------+
لاحظ أن الأعمدة التي لها DEFAULT
تظهر القيمة الافتراضية في خانة Default
الـ CHECK
هنا لدينا Constraint
مميز قليلًا وهو الـ CHECK
يسمح لك بوضع شرط معين على العمود، بحيث لا يتم قبول أي قيمة لا تحقق هذا الشرط
مثلًا، يمكنك التأكد من أن عمر الطالب أكبر من 13
أو أن قيمة العمود level
هي واحدة من المستويات المعروفة مثل Beginner
, Intermediate
, أو Advanced
يمكننا استخدام CHECK
لوضع هذه الشروط على الأعمدة عند إنشاء الجدول
CREATE TABLE Students (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(100) NOT NULL,
age INT,
level VARCHAR(50) DEFAULT 'Beginner',
email VARCHAR(100) NOT NULL UNIQUE,
CHECK (age >= 13 AND age <= 100),
CHECK (level IN ('Beginner', 'Intermediate', 'Advanced'))
);
يمكنك أيضًا إنشاء CHECK
مع اسم محدد:
CREATE TABLE Students (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(100) NOT NULL,
age INT,
level VARCHAR(50) DEFAULT 'Beginner',
email VARCHAR(100) NOT NULL UNIQUE,
CONSTRAINT chk_age CHECK (age >= 13 AND age <= 100),
CONSTRAINT chk_level CHECK (level IN ('Beginner', 'Intermediate', 'Advanced'))
);
هكذا أصبح بعض الشيوط الصارمة على مستوى الجدول
منها أن العمود age
يجب أن يكون بين 13
و 100
والعمود level
يجب أن يكون واحد من المستويات Beginner
, Intermediate
, أو Advanced
وأعطينا اسم اختياري للـ CHECK
وهو chk_age
و chk_level
الآن إذا حاول أحد إدخال طالب عمره 10
سنوات أو مستوى Expert
بهذا الشكل:
INSERT INTO Students (name, age, email)
VALUES ('Ali', 10, '[email protected]');
ستظهر رسالة خطأ تخبرنا أن القيمة غير مقبولة بسبب الـ CHECK
التي وضعناها
SQL ERROR: Check constraint 'chk_age' is violated.
ولاحظ أنه استخدم اسم الـ CHECK
الذي وضعناه وهو chk_age
ليخبرنا أن الشرط الخاص بالعمر غير متحقق
ونفس الشيء إذا حاولنا إدخال مستوى Expert
:
INSERT INTO Students (name, age, level, email)
VALUES ('Ayman', 30, 'Expert', '[email protected]');
ستظهر رسالة خطأ تخبرنا أن القيمة غير مقبولة بسبب الـ CHECK
التي وضعناها على العمود level
SQL ERROR: Check constraint 'chk_level' is violated.
ملحوظة
: في الشغل الحقيقي في الشركات لا يفضل أو غير مستحب استخدامCHECK
في قاعدة البيانات ويفضل وضع تلك القيود او الشروط في التطبيق نفسه سواء في الـFrontend
أو الـBackend
السبب يرجع إلى أن معظم الشروط تكون مرتبطة بطبيعة العمل أول بالـ Business Logic
أو حتى بالـ Business Requirements
وأحيانًا الـ Business Requirements
تتغير كل ليلة وأخرى وأحيانًا تكون معقدة ومتشابكة
بمعنى أنه في حالتنا هنا لدينا شرط أن الـ level
يجب أن يكون واحد من المستويات Beginner
, Intermediate
, أو Advanced
لكن في حالة تغير الـ Business Requirements
وأصبح لدينا مستوى جديد مثل Expert
أو Pro
فهذا يعني أننا سنضطر لتعديل الـ Database
والجداول لإضافة هذا المستوى الجديد
بالتالي سنحتاج لتعديل الـ CHECK
الذي وضعناه على العمود level
بالتالي أمر الـ CHECK
لا يتم استخدامه لأنك لا تريد أن تضطر لتعديل الجداول كلما تغير الـ Business
وبعض الأحيان تكون الشروط معقدة جدًا بحيث لا يمكن التعبير عنها بسهولة في SQL
لذا يفضل وضع تلك الشروط في التطبيق نفسه سواء في الـ Frontend
أو الـ Backend
دون الحاجة لتعديل الجداول في قاعدة البيانات
لكن بالطبع أنا اتحدث فقط عن الـ CHECK
أما بالنسبة للـ NOT NULL
, UNIQUE
, و PRIMARY KEY
، فهي قيود أساسية يجب أن تكون موجودة في قاعدة البيانات لضمان سلامة البيانات
الـ FOREIGN KEY
الـ FOREIGN KEY
هو واحد من أهم المفاهيم في قواعد البيانات
لأنه يستخدم لـ ربط جدولين معًا وضمان سلامة العلاقة بينهما
تتذكر عندما تخيلنا أننا لدينا جدول للطلاب وجدول للدورات ؟ جدول Students
وجدول Courses
قلنا أن كل طالب يمكن أن يسجل في أكثر من دورة، وكل دورة يمكن أن يكون فيه أكثر من طالب
لذا قمنا بإنشاء جدول ثالث يربط بين الطلاب والدورات وهو جدول StudentCourses
CREATE TABLE StudentCourses (
id INT PRIMARY KEY AUTO_INCREMENT,
student_id INT,
course_id INT,
enrollment_date DATE,
FOREIGN KEY (student_id) REFERENCES Students(id),
FOREIGN KEY (course_id) REFERENCES Courses(id)
);
في جدول StudentCourses
، لدينا عمودين هما student_id
و course_id
عندما تصف العمود student_id
فأنت تقول هو بمجرد id
يشير إلى id
في جدول Students
وعندما تصف العمود course_id
فأنت تقول هو مجرد id
يشير إلى id
في جدول Courses
بالتالي يمكننا أن نقول أن student_id
يشير إلى PRIMARY KEY
في جدول Students
وبالمثل course_id
يشير إلى PRIMARY KEY
في جدول Courses
وهذا تمامًا ما يفعله الـ FOREIGN KEY
فالـ FOREIGN KEY
هو key
نملكه في الجدول يشير إلى PRIMARY KEY
في جدول آخر
بالتالي في حالة جدول StudentCourses
:
student_id
هوFOREIGN KEY
يشير إلىid
في جدولStudents
course_id
هوFOREIGN KEY
يشير إلىid
في جدولCourses
ولكي نصف ونوضح هذا الترابط للـ DBMS
، نستخدم FOREIGN KEY
في تعريف الجدول
لأن الـ DBMS
لن نستنتج هذه الرابطة بمجرد وجود الأعمدة بنفس الأسماء
لذا يجب أن نخبره بشكل صريح أن student_id
هو FOREIGN KEY
يشير إلى id
في جدول Students
وأن course_id
هو FOREIGN KEY
يشير إلى id
في جدول Courses
وهذا ما فعلناه في تعريف جدول StudentCourses
FOREIGN KEY (student_id) REFERENCES Students(id),
FOREIGN KEY (course_id) REFERENCES Courses(id)
داخل الـ CREATE TABLE
نكتب FOREIGN KEY
ثم نحدد العمود الذي نريد أن يكون FOREIGN KEY
ثم نكتب REFERENCES
متبوعًا باسم الجدول الذي يشير إليه (Students)
ثم العمود الذي يشير إليه في هذا الجدول (id)
ماذا يضمن الـ FOREIGN KEY
؟
- لا يمكن إدخال
student_id
غير موجود في جدولStudents
بمعنى أنه لا يمكن أن يكون هناكstudent_id
في جدولStudentCourses
يشير إلى طالب غير موجود في جدولStudents
- لا يمكن إدخال
course_id
غير موجود في جدولCourses
بمعنى أنه لا يمكن أن يكون هناكcourse_id
في جدولStudentCourses
يشير إلى دورة غير موجودة في جدولCourses
- لا يمكن حذف طالب من جدول
Students
إذا كان مسجل في دورات
بمعنى أنه لا يمكن حذف صف من جدولStudents
إذا كان هناك صف في جدولStudentCourses
يشير إلى هذا الطالب - لا يمكن حذف دورة من جدول
Courses
إذا كان فيه طلاب مسجلين
بمعنى أنه لا يمكن حذف صف من جدولCourses
إذا كان هناك صف في جدولStudentCourses
يشير إلى هذه الدورة - يساعدنا في عمليات البحث المعقدة وسنتعمق في هذا في المقالات القادمة وخصوصًا الـ
JOIN
ملحوظة
: يوجد أنواع مختلفة للربط بين الجداول مثلONE TO ONE
,ONE TO MANY
,MANY TO MANY
لكن سنتعرف على هذه الأنواع في المقالات القادمة وأيضًا الـFOREIGN KEY
موضوع عميق وله قواعد كثيرة متعلقة بـCASCADE DELETE
,CASCADE UPDATE
وغيرها
لكن لأننا لم ندخل بعد في أوامر الـINSERT
وSELECT
وبالأخص الـDELETE
وطرق التعامل مع البيانات، فأنا لم أتطرق لهذه التفاصيل الآن
لكن لا تقلق سنتعمق في الـFOREIGN KEY
والعلاقات بين الجداول في المقالات القادمة
ملخص الـ SQL Constraints
الآن تعلمنا جميع أنواع قيود الـ SQL
الأساسية، دعونا نلخصها:
الـ Constraint |
الوصف | مثال |
---|---|---|
NOT NULL |
يمنع أن يكون العمود فارغًا | name VARCHAR(100) NOT NULL |
UNIQUE |
يضمن عدم تكرار القيم | email VARCHAR(100) UNIQUE |
PRIMARY KEY |
يمثل رقم الهوية لكل صف | id INT PRIMARY KEY |
AUTO_INCREMENT |
يجعل العمود يتزايد تلقائيًا | id INT AUTO_INCREMENT |
DEFAULT |
يضع قيمة افتراضية | country VARCHAR(50) DEFAULT 'Egypt' |
CHECK |
يتحقق من شرط معين | age INT CHECK (age >= 10) |
FOREIGN KEY |
يربط بين جدولين | student_id INT FOREIGN KEY REFERENCES Students(id) |
قد أكون تعمدت تفويت بعض التفاصيل في هذا المقال حتى لا يكون معقدًا جدًا
ولكن لا تقلق فكل شيء سنعود له بالتفصيل في المقالات القادمة
أو أنك سوف تكتشفها وتتعلم بنفسك فيما بعد سواء من هذه المقالات أو من مصادر أخرى
في المقالة القادمة، سنتعلم كيفية إدخال البيانات إلى الجداول باستخدام أمر INSERT
وكيفية استرجاع البيانات باستخدام أمر SELECT
وحينها ستفهم كيف تعمل هذه القيود عمليًا وأهميتها في حماية البيانات