ما هى أنواع الـ 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وUNIQUEFOREIGN 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, 'ahmed@university.edu', 20);
لاحظ أننا هنا نضيف طالب جديد بدون name
وتذكر أننا جعلنا name لا يقبل NULL ولا يملك قيمة افتراضية
بالتالي إن حاولنا تنفيذ هذا الأمر، ستظهر رسالة خطأ مثل:
SQL Error: Field 'name' doesn't have a default value
أعلم أننا لم نشرح الأمر INSERT بعد، لكن لا تقلق، سنغطيه في المقالة القادمة
لكن كل ما عليك أن تعرفه الآن هو أن هذا الأمر INSERT يستخدم لإضافة بيانات جديدة إلى الجدول
وهنا نحن حاولنا إضافة صف واحد في جدول Students بالبيانات التالية بالترتيب بحيث أن id هو 1 و email هو 'ahmed@university.edu' و 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', 'ahmed@university.edu', 20, 'Beginner');
INSERT INTO Students (id, name, email, age, level)
VALUES (2, 'Ahmed Ali', 'ahmed@university.edu', 22, 'Intermediate');
في هذه الحالة الطالب الأول سينجح في التسجيل بنجاح لأنه لا يوجد طالب آخر بنفس البريد الإلكتروني
ثم عندما نحاول تسجيل الطالب الثاني بنفس البريد الإلكتروني ahmed@university.edu، ستظهر رسالة خطأ
SQL ERROR: Duplicate entry 'ahmed@university.edu' 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', 'ahmed@example.com', 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, 'ali@example.com');
ستظهر رسالة خطأ تخبرنا أن القيمة غير مقبولة بسبب الـ CHECK التي وضعناها
SQL ERROR: Check constraint 'chk_age' is violated.
ولاحظ أنه استخدم اسم الـ CHECK الذي وضعناه وهو chk_age ليخبرنا أن الشرط الخاص بالعمر غير متحقق
ونفس الشيء إذا حاولنا إدخال مستوى Expert:
INSERT INTO Students (name, age, level, email)
VALUES ('Ayman', 30, 'Expert', 'ayman@example.com');
ستظهر رسالة خطأ تخبرنا أن القيمة غير مقبولة بسبب الـ 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في جدولStudentscourse_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
وحينها ستفهم كيف تعمل هذه القيود عمليًا وأهميتها في حماية البيانات