المفتاح الأساسي والمفتاح الخارجي في SQLite مع الأمثلة

⚡ ملخص ذكي

المفاتيح الأساسية والمفاتيح الخارجية في SQLite فرض سلامة البيانات من خلال تحديد كل صف بشكل فريد وربط الجداول ذات الصلة، مما يضمن وجود القيم المشار إليها دائمًا ويمنع السجلات المكررة أو الفارغة أو اليتيمة عبر قاعدة البيانات العلائقية.

  • 🔑 المفتاح الأساسي: يُعرّف المفتاح الأساسي كل صف بشكل فريد، ويجب أن تكون قيمه فريدة ولا تكون فارغة أبدًا.
  • 🧩 مفتاح مركب: يؤدي دمج عمودين أو أكثر إلى تكوين مفتاح أساسي مركب عندما لا يكون أي عمود فريدًا.
  • 🔗 مفتاح غريب: يشير المفتاح الخارجي إلى مفتاح الجدول الأصل ويفرض التكامل المرجعي بين الجداول ذات الصلة.
  • ⚙️ تفعيل الإنفاذ: SQLite يقوم هذا الأمر بتعطيل المفاتيح الخارجية افتراضيًا، لذا قم بتشغيل الأمر PRAGMA foreign_keys = ON لتفعيلها.
  • 🧱 قيود الأعمدة: تقوم قواعد NOT NULL و DEFAULT و UNIQUE و CHECK بالتحقق من صحة القيم قبل إدخالها في عمود.
  • 🤖 مساعدة الذكاء الاصطناعي: تقوم برامج المساعدة التي تعمل بالذكاء الاصطناعي لتحويل النصوص إلى SQL وبرنامج GitHub Copilot بإنشاء SQL للمفاتيح والقيود من اللغة الإنجليزية البسيطة.

المفتاح الأساسي والمفتاح الخارجي في SQLite

تشرح الأقسام التالية SQLite القيود بالتفصيل، بدءًا من المفتاح الأساسي والمفتاح الخارجي اللذين يحددان ويربطان الجداول، وتغطية قواعد NOT NULL و DEFAULT و UNIQUE و CHECK التي تتحقق من صحة البيانات في كل عمود.

SQLite القيود

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

SQLite المفتاح الأساسي

يجب أن تكون جميع القيم في عمود المفتاح الأساسي فريدة وغير فارغة. يُعرّف المفتاح الأساسي كل صف في الجدول بشكل فريد.

يمكن تطبيق المفتاح الأساسي على عمود واحد فقط أو على مجموعة من الأعمدة. في الحالة الأخيرة، يجب أن تكون مجموعة قيم الأعمدة فريدة لجميع صفوف الجدول.

بناء الجملة:

توجد عدة طرق مختلفة لتعريف المفتاح الأساسي في الجدول:

في تعريف العمود نفسه:

ColumnName INTEGER NOT NULL PRIMARY KEY;

كتعريف منفصل:

PRIMARY KEY(ColumnName);

لإنشاء مجموعة من الأعمدة كمفتاح أساسي:

PRIMARY KEY(ColumnName1, ColumnName2);

SQLite قيود NOT NULL و DEFAULT و UNIQUE و CHECK

إلى جانب المفتاح الأساسي، SQLite يُوفّر هذا النظام عدة قيود على الأعمدة للتحقق من صحة القيم المُدخلة في الجدول. يتم تعريف قيود NOT NULL وDEFAULT وUNIQUE وCHECK في تعريف العمود، ويفرض كل منها قاعدة مُحددة على العمود. نوع البيانات والقيم.

ليس القيد فارغة

استخدم SQLite يمنع قيد NOT NULL عمودًا من أن يحتوي على قيمة فارغة (null):

ColumnName INTEGER  NOT NULL;

القيد الافتراضي

مع SQLite في حالة استخدام قيد القيمة الافتراضية، إذا لم تقم بإدخال أي قيمة في عمود، فسيتم إدخال القيمة الافتراضية بدلاً من ذلك.

فمثلا:

ColumnName INTEGER DEFAULT 0;

إذا كتبت عبارة إدراج ولم تحدد أي قيمة لهذا العمود، فسيكون للعمود القيمة 0.

القيد الفريد

استخدم SQLite يمنع قيد UNIQUE القيم المكررة بين جميع قيم العمود.

فمثلا:

EmployeeId INTEGER NOT NULL UNIQUE;

يضمن هذا أن تكون قيمة "EmployeeId" فريدة؛ فلا يُسمح بتكرار القيم. يُرجى ملاحظة أن هذا ينطبق على قيم عمود "EmployeeId" فقط.

التحقق من القيد

استخدم SQLite يُحدد قيد التحقق شرطًا للتحقق من القيمة المُدخلة. إذا لم تتطابق القيمة مع الشرط، فلن يتم إدخالها.

Quantity INTEGER NOT NULL CHECK(Quantity > 10);

لا يمكنك إدخال قيمة أقل من 10 في عمود "الكمية".

SQLite مفتاح غريب

استخدم SQLite المفتاح الخارجي هو قيد يتحقق من وجود قيمة موجودة في جدول ما في جدول آخر له علاقة بالجدول الأول الذي تم فيه تعريف المفتاح الخارجي.

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

في هذه الحالة، عندما تحاول إدخال قيمة في ذلك العمود، سيضمن المفتاح الخارجي وجود القيمة المدخلة في عمود الجدول المشار إليه.

لاحظ أن قيود المفتاح الخارجي غير مفعلة افتراضيًا في SQLiteيجب عليك تفعيلها أولاً عن طريق تشغيل الأمر التالي:

PRAGMA foreign_keys = ON;

تم تقديم قيود المفاتيح الخارجية في SQLite بدءًا من الإصدار 3.6.19.

مثال SQLite مفتاح غريب

لنفترض أن لدينا جدولين: الطلاب والأقسام.

يحتوي جدول الطلاب على قائمة بالطلاب، بينما يحتوي جدول الأقسام على قائمة بالأقسام. ينتمي كل طالب إلى قسم معين؛ أي أن لكل طالب عمودًا خاصًا بمعرف القسم.

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

لذا، إذا قمنا بإنشاء قيد مفتاح خارجي على DepartmentId في جدول الطلاب، فيجب أن يكون كل departmentId مدرج موجودًا في جدول الأقسام.

CREATE TABLE [Departments] (
	[DepartmentId] INTEGER  NOT NULL PRIMARY KEY AUTOINCREMENT,
	[DepartmentName] NVARCHAR(50)  NULL
);
CREATE TABLE [Students] (
	[StudentId] INTEGER  PRIMARY KEY AUTOINCREMENT NOT NULL,
	[StudentName] NVARCHAR(50)  NULL,
	[DepartmentId] INTEGER  NOT NULL,
	[DateOfBirth] DATE  NULL,
	FOREIGN KEY(DepartmentId) REFERENCES Departments(DepartmentId)
);

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

في هذا المثال، يرتبط جدول الأقسام بجدول الطلاب عبر مفتاح خارجي، لذا يجب أن تكون أي قيمة لـ departmentId مُدرجة في جدول الطلاب موجودة في جدول الأقسام. إذا حاولتَ إدراج قيمة departmentId غير موجودة في جدول الأقسام، فسيمنعك قيد المفتاح الخارجي من القيام بذلك.

لنقم بإضافة قسمين، "تكنولوجيا المعلومات" و"الفنون"، إلى جدول الأقسام بالصيغة التالية استعلامات الإدراج:

INSERT INTO Departments VALUES(1, 'IT');
INSERT INTO Departments VALUES(2, 'Arts');

يجب أن تُضيف العبارتان قسمين إلى جدول الأقسام. يمكنك التأكد من إضافة القيمتين بتشغيل الاستعلام "SELECT * FROM Departments" بعد ذلك.

نتيجة استعلام SELECT التي تُظهر أقسام تكنولوجيا المعلومات والفنون في SQLite

ثم حاول إدخال طالب جديد برقم قسم غير موجود في جدول الأقسام:

INSERT INTO Students(StudentName,DepartmentId) VALUES('John', 5);

لن يتم إدراج الصف، وستتلقى خطأً يقول: فشل قيد المفتاح الخارجي.

SQLite رسالة خطأ: فشل قيد المفتاح الخارجي

الفرق بين المفتاح الأساسي والمفتاح الأجنبي SQLite

تُساهم المفاتيح الأساسية والمفاتيح الخارجية في الحفاظ على سلامة البيانات، لكن لكل منهما دور مختلف. يُحدد المفتاح الأساسي الصفوف داخل جدول واحد، بينما يربط المفتاح الخارجي الصفوف بين جدولين مرتبطين. يُلخص الجدول أدناه الفروقات الرئيسية بينهما.

قاعدة المفتاح الأساسي مفتاح غريب
الهدف يُعرّف كل صف في جدوله الخاص بشكل فريد يشير إلى المفتاح الأساسي لجدول آخر لربطهما
تميز يجب أن تكون القيم فريدة قد تتكرر القيم، لذا يمكن أن تشترك العديد من الصفوف الفرعية في أصل واحد.
القيم الفارغة لا يمكن أن يكون فارغًا يمكن أن تكون القيمة فارغة عندما تكون العلاقة اختيارية
عدد الطاولات مفتاح أساسي واحد فقط لكل جدول يمكن أن يحتوي الجدول على عدة مفاتيح خارجية
الفهرسة تمت الفهرسة تلقائيًا غير مفهرسة تلقائيًا؛ أضف واحدة لتحسين الأداء

في مثال الطلاب والأقسام، يكون DepartmentId هو المفتاح الأساسي لجدول الأقسام ومفتاح خارجي في جدول الطلاب، وهو ما يربط كل طالب بقسم صالح.

SQLite المفتاح الأساسي المركب

المفتاح الأساسي المركب هو مفتاح أساسي يتكون من عمودين أو أكثر. يُستخدم عندما لا يكون أي عمود فريدًا بمفرده، ولكن تركيبة الأعمدة تكون فريدة لكل صف. SQLite يعامل القيم المجمعة كمفتاح واحد.

على سبيل المثال، قد يسمح جدول التسجيل للطالب نفسه في العديد من الدورات الدراسية، وللدورة الدراسية نفسها للعديد من الطلاب، ومع ذلك يجب أن يظهر كل زوج من الطالب والدورة الدراسية مرة واحدة فقط:

CREATE TABLE Enrollments (
	StudentId INTEGER NOT NULL,
	CourseId INTEGER NOT NULL,
	Grade TEXT,
	PRIMARY KEY (StudentId, CourseId)
);

هنا، لا يُعدّ كلٌّ من StudentId وCourseId فريدًا بمفرده، ولكن الزوج (StudentId, CourseId) فريد، لذا لا يمكن تسجيل الطالب نفسه في المقرر نفسه مرتين. يُرجى مراعاة النقاط التالية عند استخدام مفتاح مُركّب:

  • استخدم مفتاحًا مركبًا عندما لا يستطيع عمود واحد تحديد صف بشكل فريد.
  • كل عمود في المفتاح المركب يتبع قواعد المفتاح الأساسي، لذلك يجب أن تكون القيمة المجمعة فريدة وليست فارغة.
  • يتم كتابة المفتاح المركب كبند PRIMARY KEY منفصل على مستوى الجدول، وليس داخل تعريف عمود واحد.

SQLite إجراءات المفتاح الخارجي: عند الحذف وعند التحديث

يمكن للمفتاح الخارجي أيضًا التحكم في ما يحدث للصفوف الفرعية عند حذف أو تحديث الصف الأصل الذي يشير إليه. تُضاف هذه الإجراءات المرجعية باستخدام عبارتي ON DELETE و ON UPDATE عند تعريف المفتاح الخارجي. SQLite يدعم خمسة إجراءات:

  • لا يوجد إجراء — الإجراء الافتراضي، الذي يثير خطأً إذا كانت الصفوف الفرعية لا تزال تشير إلى الأصل.
  • تقييد — يمنع الحذف أو التحديث فورًا، قبل تنفيذ أي تغيير آخر.
  • تعيين NULL — يقوم بتعيين عمود المفتاح الخارجي الفرعي إلى قيمة فارغة.
  • الوضع الإفتراضي — يقوم بتعيين عمود المفتاح الخارجي الفرعي إلى قيمته الافتراضية المعلنة.
  • تتالي — يطبق نفس التغيير على صفوف الأبناء، لذا فإن حذف أحد الأبناء يؤدي إلى حذف أبنائه.

يُعيد المثال أدناه إنشاء جدول الطلاب بحيث يؤدي حذف قسم إلى حذف طلابه تلقائيًا، كما يؤدي تحديث معرف القسم إلى تحديث الطلاب المطابقين:

CREATE TABLE Students (
	StudentId INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
	StudentName NVARCHAR(50) NULL,
	DepartmentId INTEGER NOT NULL,
	FOREIGN KEY(DepartmentId) REFERENCES Departments(DepartmentId)
		ON DELETE CASCADE
		ON UPDATE CASCADE
);

تذكر أن الإجراءات المرجعية لا تُنفذ إلا عند تفعيل دعم المفاتيح الخارجية، لذا قم بتشغيل الأمر PRAGMA foreign_keys = ON في بداية كل اتصال. بدون ذلك، SQLite يقوم بتحليل عبارات ON DELETE و ON UPDATE ولكنه لا يفرضها.

الأسئلة الشائعة

نعم. عندما يتم تعريف عمود واحد بالضبط على أنه INTEGER PRIMARY KEY، فإنه يصبح اسمًا بديلاً لـ rowid المضمن في الجدول. SQLite لا يحتفظ بفهرس منفصل له، لذا فإن عمليات البحث بواسطة هذا المفتاح سريعة ولا تستخدم مساحة تخزين إضافية.

يبقى فرض استخدام المفاتيح الخارجية معطلاً افتراضيًا للحفاظ على التوافق مع قواعد البيانات القديمة والبرامج النصية المكتوبة قبل الإصدار 3.6.19. يجب تشغيل الأمر PRAGMA foreign_keys = ON قبل كل اتصال بقاعدة البيانات. SQLite يبدأ بفحص قيود المفتاح الخارجي.

SQLite يقوم البرنامج بفهرسة المفاتيح الأساسية والأعمدة الفريدة تلقائيًا، ولكنه لا يفهرس أعمدة المفاتيح الخارجية. ولأن العمود الفرعي يُقرأ في كل عملية فحص للقيود، يُنصح بإنشاء فهرس خاص بك على كل عمود من أعمدة المفاتيح الخارجية لتحسين الأداء.

لا. ALTER TABLE in SQLite لا يمكن إضافة مفتاح أساسي أو مفتاح خارجي إلى جدول موجود. يمكنك إعادة تسمية الجدول القديم، وإنشاء جدول جديد مع تحديد المفتاح، ونسخ الصفوف باستخدام أمر INSERT SELECT، ثم حذف الجدول القديم.

يُعيّن المفتاح الأساسي البسيط من نوع عدد صحيح المعرّف التالي برقم أعلى من أكبر معرّف موجود، ويمكنه إعادة استخدام المعرّفات بعد عمليات الحذف. الترقيم التلقائي tracks هو أعلى معرف تم استخدامه على الإطلاق في sqlite_sequence ولا يعيد استخدام أي قيمة، بتكلفة أداء صغيرة.

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

نعم. تقوم برامج الذكاء الاصطناعي لتحويل النصوص إلى لغة SQL بتحويل وصف بسيط باللغة الإنجليزية لجداولك إلى عبارات CREATE TABLE مع بنود PRIMARY KEY و FOREIGN KEY. يُحسّن توفير المخطط الحالي دقة النتائج، ويجب دائمًا مراجعة استعلامات SQL المُولّدة قبل تشغيلها على بيانات حقيقية.

نعم. مساعد الطيار جيثب يقترح استخدام كود CREATE TABLE مع PRIMARY KEY و FOREIGN KEY وقيود أخرى مضمنة في محررات مثل VS Code. يقوم بقراءة المخطط والترحيلات الموجودة لديك، لذا فإن عمليات الإكمال الخاصة به تعيد استخدام أسماء الجداول والأعمدة الحقيقية.

تلخيص هذه التدوينة بـ: