نريد حفظ حجز يربط طالباً بجهاز وفترة استخدام، ثم نريد استرجاعه دون فقد العلاقة بين هذه البيانات. سنبني الجداول لهذا المثال في SQLite، ونضيف القيود التي تمنع مراجع غير موجودة أو فترات غير صالحة، ثم نجرب استعلام التعارض. المثال للتعلم وإعادة التشغيل محلياً؛ قاعدة الإنتاج وآلية التعامل مع الطلبات المتزامنة تحتاجان اختياراً يناسب متطلبات النظام.
حوّل الوصف إلى حقائق وقيود
افترض أن كل حجز يتعلق بجهاز واحد، وأن الجهاز يمكن أن يملك حجوزات كثيرة في أوقات مختلفة، وأن المستخدم يستطيع إنشاء أكثر من حجز. لا تضع قائمة معرفات الأجهزة داخل خلية نصية إذا كان الحجز قد يشمل عدة أجهزة؛ عندها تحتاج كياناً وسيطاً وسياسة واضحة. في نطاق هذا المثال نبقي الجهاز الواحد لتظل قواعد الزمن قابلة للشرح.
- اسم الجهاز صفة للجهاز، وليس نصاً ينسخ مع كل حجز.
- معرف المستخدم يربط الحجز بهوية موجودة، ولا يستبدله اسم يمكن تكراره.
- الفترات تحفظ بتنسيق UTC ثابت؛ تعرض الواجهة الوقت المحلي.
- الحالة واحدة من confirmed أو cancelled وفق نطاق المثال.
- قاعدة منع التداخل تحتاج تحققاً آمناً من التزامن، وليست مجرد شكل للشاشة.
مخطط العلاقات قبل SQL
users 1 ───────── N bookings N ───────── 1 devices
id PK id PK id PK
display_name user_id FK name
device_id FK
starts_at
ends_at
status
الخطوط تعني أن كل حجز يربط مستخدماً واحداً بجهاز واحد. لا يعني ذلك أن حذف المستخدم يجب أن يحذف تاريخ الحجوزات تلقائياً؛ اخترنا منع الحذف الذي يكسر المرجع، ويمكن للنظام الفعلي تعطيل الحساب أو إخفاء بياناته وفق سياسة مناسبة. قرار الحذف جزء من المتطلبات، لا تفصيل يؤجل حتى تظهر أول رسالة خطأ.
أنشئ الجداول وفعّل العلاقات
شغّل المقطع في قاعدة تجربة. توضح وثائق SQLite أن فحص المفاتيح الأجنبية يحتاج تفعيله لكل اتصال؛ لا تعتمد على افتراض الإعداد الافتراضي. صيغة الوقت هنا نص UTC ثابت الطول، وعلى الخادم التحقق من صلاحية التاريخ قبل الحفظ؛ المقارنة النصية وحدها لا تثبت أن اليوم صالح في التقويم.
PRAGMA foreign_keys = ON;
CREATE TABLE users (
id INTEGER PRIMARY KEY,
display_name TEXT NOT NULL
);
CREATE TABLE devices (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL UNIQUE
);
CREATE TABLE bookings (
id INTEGER PRIMARY KEY,
user_id INTEGER NOT NULL REFERENCES users(id),
device_id INTEGER NOT NULL REFERENCES devices(id),
starts_at TEXT NOT NULL,
ends_at TEXT NOT NULL,
status TEXT NOT NULL DEFAULT 'confirmed'
CHECK (status IN ('confirmed', 'cancelled')),
CHECK (ends_at > starts_at)
);
CREATE INDEX bookings_device_time
ON bookings(device_id, starts_at, ends_at);
المفتاح الأساسي يمنح كل صف هوية مستقرة، وNOT NULL يرفض غياب الحقول اللازمة، وUNIQUE يمنع اسم جهاز مكرر في هذا النموذج. إن كان الاسم قابلاً للتكرار في مشروعك، استبدل هذا القيد برمز جهاز فريد. راجع مرجع CREATE TABLE قبل تحويل قرار تجاري إلى قيد عام.
أدخل بيانات واختبر سؤالاً واضحاً
INSERT INTO users VALUES (1, 'طالب تجريبي');
INSERT INTO devices VALUES (12, 'جهاز مختبر A');
INSERT INTO bookings
(id, user_id, device_id, starts_at, ends_at)
VALUES
(801, 1, 12, '2026-11-01T06:00:00Z',
'2026-11-01T07:00:00Z');
SELECT b.id, u.display_name, d.name, b.starts_at
FROM bookings b
JOIN users u ON u.id = b.user_id
JOIN devices d ON d.id = b.device_id
WHERE b.status = 'confirmed';
للفترة نصف المفتوحة [البداية، النهاية)، يتداخل حجز موجود مع طلب جديد إذا كانت بداية الموجود قبل نهاية الجديد ونهايته بعد بداية الجديد. هذا يسمح لحجز أن يبدأ تماماً عند انتهاء سابقه. يجب الاتفاق على هذه القاعدة قبل كتابة الشاشة، لأن إدخال مساواة في المقارنة يغير سلوك الحدود.
SELECT EXISTS (
SELECT 1 FROM bookings
WHERE device_id = :device
AND status = 'confirmed'
AND starts_at < :new_end
AND ends_at > :new_start
) AS has_conflict;
نتائج الفحص وحدود المثال
فُحصت المقاطع على SQLite من خلال Python 3.12: إنشاء الجداول وإدخال المثال وإرجاع صف الحجز، ورفض مرجع جهاز غير موجود، ورفض نهاية قبل البداية وحالة غير معروفة. واختبر استعلام التداخل بفترة داخل الحجز وأخرى تلامس نهايته دون تداخل. هذه فحوص قاعدة محلية، ولا تثبت أمان نظام حجز متعدد المستخدمين.
استعلام التداخل لا يمنع السباق تلقائياً؛ قد يصل طلبان قبل حفظ أي منهما. يجب أن يحمي الخادم عملية الفحص والحفظ بمعاملة وآلية تناسب قاعدة الإنتاج. كذلك لا يتضمن المثال جلسات دخول أو صلاحيات أو سياسات صيانة أو ترحيلاً من قاعدة قائمة، فلا تصفه بأنه نظام إنتاج مكتمل.
راجع التصميم مع تطور النطاق
راجع ما يحدث عند إلغاء الحجز أو تغيير اسم الجهاز أو إيقافه أو فقدان الاتصال أثناء الحفظ، وسجّل هذه القرارات في متطلبات المشروع. احفظ الترحيلات وبيانات التجربة التي تعيد بها الاختبار. وعند طلب تصميم قاعدة بيانات وواجهات API، أرفق قواعد العمل والجداول الموجودة وعينة بيانات منقحة، حتى تشمل المراجعة العلاقات والقيود والتكاملات المطلوبة.
المصادر
مراجعة المصادر: .
اختبار المثال وحدوده
نُفذت مقاطع SQL واستعلام التداخل واختبارات رفض القيود على Python 3.12/SQLite محلياً؛ لم يُختبر تزامن إنتاج أو خادم حجز كامل.
مقالات ذات صلة
الخدمة المرتبطة: تطوير قواعد البيانات وواجهات البرمجة.
أرسل متطلبات مشروعك