أساسيات قواعد البيانات

قاعدة البيانات (Database) مجموعة منظَّمة ودائمة من البيانات: تبقى بعد توقف البرنامج الذي كتبها، ولهذا تجد طلباتك في مكانها حين تعيد فتح تطبيق التسوق. أما البرنامج الذي يخزّن البيانات ويفرض قواعدها ويجيب عن الاستعلامات فهو نظام إدارة قواعد البيانات (DBMS)، مثل PostgreSQL وMySQL وSQL Server وSQLite؛ وكثيراً ما يسمّي الناس الاثنين «قاعدة البيانات».

وفي قاعدة البيانات العلائقية (Relational database) تُحفظ البيانات في جداول: كل جدول يضم نوعاً واحداً من الأشياء، وكل صف (Row) سجل واحد، وكل عمود (Column) سمة واحدة لها نوع بيانات (Data type) مُعلَن. فحين تسجّل في متجر إلكتروني يضيف التطبيق صفاً واحداً إلى جدول customers — العميل ⁦317⁩، سارة أحمد، وهاتفها 0550000001 — وكل شاشة تعرض ملفك لاحقاً تقرأ هذا الصف.

لماذا يهم

تعرض الشاشة ما اختار التطبيق أن يعرضه، أما قاعدة البيانات فتحفظ ما خُزِّن فعلاً. وكثير من العيوب لا يوجد إلا هناك: رقم هاتف فقد صفره البادئ، واسم عربي حُفظ علامات استفهام، ووصف قُطع عند ⁦50⁩ حرفاً، وطابع زمني انزاح ثلاث ساعات. وتظهر هذه العيوب بعد أسابيع في فاتورة أو تقرير أو نظام آخر.

ومعرفة طريقة بناء الجدول تحوّل التخمين إلى تصميم اختبار؛ فنوع كل عمود وطوله حدود نادراً ما يذكرها المتطلب، وكل عمود اختياري يطرح سؤالاً: ماذا يعني «فارغ»؟

الجداول والصفوف والأعمدة

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

وهناك خاصيتان تفاجئان المبتدئين. الأولى أن للعمود المعنى نفسه في كل صف: فلا يحمل phone رقم هاتف في صف وبريداً إلكترونياً في صف آخر. والثانية أنه لا ترتيب ذاتياً للصفوف: فمن دون ORDER BY قد تعيدها قاعدة البيانات بأي ترتيب، لذا فإن «آخر صف» ليس طريقة للعثور على السجل الذي أنشأته للتو. ابحث عنه بمعرّفه — هنا customer_id، وهو مفتاح (Key) وموضوع درس المفاتيح والعلاقات.

جدول customers: كل صف عميل واحد، وكل عمود سمة واحدة بنوع بيانات واحد، وNULL في birth_date تعني أنه لم تُخزَّن قيمة، بخلاف الرصيد ⁦0.00⁩ فهو قيمة.
customer_id · INTEGERالأعمدة: لكل عمود سمة واحدة ونوع بياناتها
full_nameVARCHAR(100)phoneVARCHAR(20)birth_dateDATEwallet_balanceNUMERIC(10,2)
317Sara Ahmed0550000001نص: يبقى الصفر البادئ1994-03-18250.00
318Ravi Kumar+919800000002نص: تبقى علامة +NULLلم تُخزَّن قيمة0.00الصفر قيمة
319Lina Haddad07900000032001-11-021250.50
  • الصفعميل واحد، في كل عمود منه قيمة أو NULL
  • NULL: لا قيمة، وهو ليس صفراً ولا نصاً فارغاً

صفوف خيالية. لا ترتيب ذاتياً للصفوف، وقد حُذف عمودان من تعريف الجدول في المثال التطبيقي.

  • أسماء أخرى: الجدول في النظرية العلائقية علاقة (Relation)، والصف سجل (Record) أو Tuple، والعمود حقل (Field) أو سمة (Attribute).
  • المخطط (Schema): تعريف جميع الجداول والأعمدة والأنواع والقواعد (وفي PostgreSQL وSQL Server هو أيضاً مساحة أسماء تجمع الجداول).
النسخة النصية
Table: customers

customer_id | full_name   | phone         | birth_date | wallet_balance
------------+-------------+---------------+------------+---------------
        317 | Sara Ahmed  | 0550000001    | 1994-03-18 |         250.00
        318 | Ravi Kumar  | +919800000002 | NULL       |           0.00
        319 | Lina Haddad | 0790000003    | 2001-11-02 |        1250.50

أنواع البيانات: ما الذي يقبله العمود

يُعلن كل عمود نوع بيانات يحدد القيم التي يقبلها، وكيف تُرتَّب وتُقارَن، وما أقصى حجم لها. وتختلف الأسماء والصرامة بين المحرّكات: يستخدم SQL Server النوع BIT بدلاً من BOOLEAN، ويرفض PostgreSQL النصَّ أو العدد الذي يتجاوز ما يتسع له عموده، لكنه يقرّب المنازل العشرية الزائدة دون أي خطأ، ويتوقف سلوك MySQL على وضع SQL المضبوط (SQL mode)، ويخزّن SQLite افتراضياً أي قيمة تقريباً في أي عمود.

  • الأعداد الصحيحة — INTEGER وBIGINT: للعدّ والكميات والمعرّفات. لكل منها نطاق ثابت، وINTEGER ذو الأربعة بايتات يتوقف عند ⁦2147483647⁩.
  • الأعداد العشرية الدقيقة — NUMERIC(p,s) أو DECIMAL(p,s): للمال. فـNUMERIC(10,2) فيه ⁦10⁩ أرقام إجمالاً، منها ⁦2⁩ بعد الفاصلة: أقصى ما يتسع له ⁦99999999.99⁩، ويخزّن PostgreSQL القيمة ⁦0.125⁩ على أنها ⁦0.13⁩.
  • الأعداد التقريبية — REAL وDOUBLE PRECISION: للقياسات، ولا تصلح للمال أبداً؛ ففي الفاصلة العائمة الثنائية لا يساوي 0.1 + 0.2 القيمة 0.3 بالضبط.
  • النصوص — VARCHAR(n) وTEXT: الرقم n هو الطول الأقصى. تعدّ معظم المحرّكات الأحرف، لكن بعض الأنواع تعدّ البايتات، وفي ترميز ⁦UTF-8⁩ يشغل الحرف العربي بايتين وحرف الديفاناغاري ثلاثة بايتات.
  • التاريخ والوقت — DATE وTIMESTAMP: يوم أو لحظة. تحقّق هل يحتفظ الطابع الزمني بالمنطقة الزمنية؛ فالساعة ⁦01:30⁩ في الرياض (⁦UTC+3⁩) ما زالت في اليوم السابق بتوقيت UTC.
  • القيم المنطقية — BOOLEAN: صواب أو خطأ، ويخزّنه MySQL عدداً صحيحاً صغيراً فترى ⁦1⁩ و⁦0⁩.
  • NULL — ليس نوعاً بل علامة تعني «لا قيمة» في أي عمود. وهو يختلف عن الصفر، وعن النص الفارغ ''، وعن كلمة «NULL» يكتبها المستخدم. والعمود المُعلَن NOT NULL إلزامي.

جرّبها: محوّل CSV إلى SQL جرّبها: مولّد مجموعات الأرقام الحدّية جرّبها: عدّاد طول السلاسل النصية

مثال تطبيقي: أفكار اختبار من تعريف جدول

صفحة ملف شخصي جديدة يُدخل فيها العملاء الاسم ورقم الجوال وتاريخ الميلاد اختيارياً، وتعرض رصيد المحفظة. وقبل أن توجد أي شاشة، اقرأ تعريف الجدول الذي شاركه المطور أدناه بوصفه جزءاً من أساس الاختبار (Test basis)، أي كل ما تشتق منه اختباراتك، وحوّل أعمدته إلى شروط اختبار (Test conditions)، أي الجوانب التي تستحق الاختبار، أو إلى أسئلة:

  • full_name VARCHAR(100): احفظ أسماء طولها ⁦100⁩ و⁦101⁩ حرف (الحد الأقصى وحرف واحد بعده) بالإنجليزية والعربية والهندية. المتوقع أن يُخزَّن الاسم ذو ⁦100⁩ حرف دون تغيير، وأن يُرفض ذو ⁦101⁩ برسالة واضحة لا أن يُقطع بصمت.
  • phone VARCHAR(20): النص هو النوع الصحيح؛ فالنوع الرقمي يُسقط الصفر البادئ من 0550000001 وعلامة + من +919800000002. قارن القيمة المخزّنة حرفاً حرفاً.
  • birth_date DATE NULL: اترك الحقل فارغاً. المتوقع NULL في الصف، لا قيمة بديلة مثل 1900-01-01.
  • wallet_balance NUMERIC(10,2): اشحن ⁦0.10⁩ ثلاث مرات وتوقّع ⁦0.30⁩ بالضبط. واسأل ماذا يحدث حين يرفع الشحن الرصيد فوق ⁦99999999.99⁩، وهي أقصى قيمة يتسع لها العمود.
  • created_at TIMESTAMP: هذا النوع في PostgreSQL لا يحتفظ بالمنطقة الزمنية. سجّل عميلاً الساعة ⁦01:30⁩ بتوقيت الرياض واسأل: هل خُزّن الوقت بتوقيت UTC أم بالتوقيت المحلي؟ فكل تقرير يومي يتوقف على الإجابة.
CREATE TABLE customers (
  customer_id     INTEGER        NOT NULL,
  full_name       VARCHAR(100)   NOT NULL,
  phone           VARCHAR(20)    NOT NULL,
  birth_date      DATE           NULL,
  wallet_balance  NUMERIC(10,2)  NOT NULL DEFAULT 0,
  is_active       BOOLEAN        NOT NULL DEFAULT TRUE,
  created_at      TIMESTAMP      NOT NULL,
  CONSTRAINT pk_customers PRIMARY KEY (customer_id)
);

جرّبها: مولّد مجموعات بيانات SQL جرّبها: مولّد مجموعات بيانات Unicode

قواعد البيانات العلائقية وغيرها

قواعد البيانات العلائقية، التي يُستعلم عنها بلغة SQL، واسعة الاستخدام لحفظ السجلات التجارية، وهي محور هذه الوحدة. ولغة SQL موحّدة في المعيار ⁦ISO/IEC 9075⁩، لكن لكل محرّك لهجته. أما قواعد البيانات غير العلائقية (NoSQL) فتنظّم البيانات بطرق أخرى، وكثيراً ما تترك الفحوص للتطبيق:

  • قواعد بيانات المستندات (Document databases)، مثل MongoDB، تحفظ مستندات شبيهة بـJSON قد تختلف حقولها داخل المجموعة (Collection) الواحدة ما لم تمنع ذلك قاعدةُ تحقق؛ فاختبر الحقول المفقودة أو ذات الأسماء المكتوبة خطأً والأرقام المخزّنة نصاً.
  • مخازن المفتاح والقيمة (Key-value stores)، مثل Redis، تحفظ قيمة تحت مفتاح، وكثيراً ما تُستخدم ذاكرةً مؤقتة (Cache) قد لا تعود مطابقة لقاعدة البيانات التي تقف خلفها.
  • مخازن الأعمدة العريضة (Wide-column stores)، مثل Apache Cassandra، توزّع جداول ضخمة جداً على خوادم كثيرة.
  • قواعد بيانات الرسوم البيانية (Graph databases)، مثل Neo4j، تحفظ العُقد والعلاقات بينها، مثل من يتابع من.

جرّبها: محوّل JSON إلى SQL

أخطاء شائعة

  • اعتبار الشاشة دليلاً على ما خُزِّن؛ فقد تعيد الصفحة عرض ما كتبته بينما يحمل الصف قيمة مقطوعة أو مشوّهة.
  • البحث عن سجل جديد بأخذ «آخر صف» بدلاً من البحث عنه بمفتاحه.
  • معاملة NULL والصفر والنص الفارغ معاملةً واحدة في بيانات الاختبار وفي النتائج المتوقعة.
  • قبول نوع رقمي لكل ما يتكوّن من أرقام: أرقام الهواتف والباركود وأرقام الهوية نصوص، لأن الأصفار البادئة والتنسيق مهمة.
  • تنفيذ UPDATE أو DELETE على قاعدة اختبار مشتركة «لإصلاح» البيانات دون موافقة مالكها.

من منظور ضمان الجودة

في جلسات التنقيح (Backlog refinement) أو مراجعة التصميم، اطلب تعريف الجدول للميزة الجديدة واقرأه جزءاً من أساس الاختبار. كل عمود يطرح الأسئلة نفسها — إلزامي أم اختياري، وأي نوع، وأي طول أو نطاق، وأي منطقة زمنية، وما القيمة الافتراضية — والأفضل أن يُطرح أي تعارض، كنموذج يقبل ⁦150⁩ حرفاً لعمود من ⁦100⁩ حرف، قبل البرمجة.

وأثناء الاختبار، اطلب صلاحية قراءة فقط على قاعدة بيانات الاختبار وتأكّد مما خزّنه كل إجراء. وفي تقرير الخطأ اذكر الجدول والعمود ومفتاح الصف والقيمة المتوقعة والقيمة الفعلية: «customers.phone للعميل ذي customer_id ⁦318⁩ قيمته 919800000002، والمتوقع +919800000002».

تمرين عملي

اختر أنواع البيانات لجدول منتجات

المطلوب

يريد متجر استيراد قائمة المنتجات أدناه إلى جدول جديد اسمه products. الباركود رمز ⁦EAN-13⁩ من ⁦13⁩ رقماً وقد يبدأ بصفر. صمّم الجدول، ثم اكتب فكرة اختبار واحدة لكل عمود.

بعد ذلك قارن اختياراتك بالأنواع التي تستنتجها أداة CSV to SQL من العينة: فهي تخمين من القيم، لا قرار مبني على المعنى.

المادة
sku,name_en,name_ar,price_sar,stock_qty,weight_kg,launch_date,online,barcode
TEA-001,Green tea,شاي أخضر,18.50,240,0.125,2026-03-01,yes,0614141000012
CUP-014,Glass cup,كوب زجاجي,9.95,0,0.250,2025-11-20,no,0614141000029
الخطوات
  1. صنّف كل عمود: رقم تحسب به، أم رمز من أرقام، أم نص، أم تاريخ، أم «نعم أو لا».
  2. اختر نوعاً لكل عمود، مع الطول أو الدقة حيث يلزم، وحدّد هل هو إلزامي أم اختياري.
  3. اختر العمود الذي يحدد الصف، ثم اكتب فكرة اختبار واحدة لكل عمود مع نتيجتها المتوقعة.
إظهار الإجابة النموذجية
الإجابة النموذجية

تصميم معقول بأنواع PostgreSQL: sku VARCHAR(20) NOT NULL (يحدد الصف)، وname_en VARCHAR(200) NOT NULL، وname_ar VARCHAR(200) NOT NULL، وprice_sar NUMERIC(10,2) NOT NULL، وstock_qty INTEGER NOT NULL، وweight_kg NUMERIC(8,3) NULL، وlaunch_date DATE NULL، وis_online BOOLEAN NOT NULL (العمود online بعد إعادة تسميته ليُقرأ «نعم أو لا»)، وbarcode VARCHAR(13) NULL.

الفخاخ: الباركود رمز لا رقم، فلو خُزّن بالنوع BIGINT لفقد 0614141000012 صفره البادئ، لذا يجب أن يكون نصاً. والسعر مال، فيحتاج نوعاً عشرياً دقيقاً لا REAL أبداً. وعمود «نعم أو لا» يصبح BOOLEAN، ويجب أن يحدد الاستيراد معنى «Yes» أو «Y».

أفكار الاختبار: يُرفض رمز SKU المكرر؛ ويُخزَّن الاسمان بطول ⁦200⁩ حرف دون تغيير؛ ويبقى السعر ⁦18.50⁩ كما هو بالضبط؛ ويُخزَّن المخزون ⁦0⁩ صفراً ويُرفض المخزون الفارغ؛ ويحتفظ الوزن ⁦0.125⁩ بثلاث منازل عشرية؛ ويصبح تاريخ الإطلاق الفارغ NULL؛ وتصبح «no» القيمة false؛ ويحتفظ الباركود بصفره البادئ، ويُرفض باركود من ⁦12⁩ رقماً — فالنوع VARCHAR(13) وحده سيقبله، لذا اسأل: هل يفرض فحصٌ في قاعدة البيانات أو في الاستيراد ⁦13⁩ رقماً بالضبط؟

تحدد أداة CSV to SQL أنواعها من هذين الصفين فقط: فتقترح DECIMAL(4,2) للسعر، وهو يتوقف عند ⁦99.99⁩، وVARCHAR(20) للاسمين، وهو أقصر من أسماء المنتجات الحقيقية. لكنها تُبقي الباركود نصاً بسبب الصفر البادئ. فتعامل مع الأنواع المستنتجة على أنها مسودة أولى تراجعها، لا على أنها التصميم.

أسئلة لمالك المنتج: هل يُستخدم الوزن لحساب رسوم الشحن؟ هل يمكن أن يشترك منتجان في باركود واحد؟ ما أطول اسم منتج حقيقي؟

أسئلة شائعة

هل أحتاج إلى SQL لاختبار قاعدة البيانات؟

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

ما الفرق بين قاعدة البيانات والجدول؟

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

هل NoSQL أفضل من قاعدة البيانات العلائقية؟

ليس أحدهما أفضل من الآخر بإطلاق. فلكل منهما مفاضلات مختلفة في البنية والاستعلام والتوسّع، ومنتجات كثيرة تستخدم الاثنين. وبصفتك مختبِراً، اعرف أي مخزن يحفظ أي بيانات، وأي القواعد يفرضها كل مخزن بنفسه، وأيها يتركه للتطبيق.

المراجع

تدرّب بأدوات متقن

تحويل بيانات CSV إلى جمل CREATE TABLE وINSERT مع استنتاج أنواع الأعمدة.

قواعد البيانات

ولّد سلاسل نصية اختبارية بترميز Unicode عبر الخطوط (العربية والديفاناغارية وCJK والسيريلية) والرموز التعبيرية وعلامات الدمج وصيغ التطبيع.

بيانات الاختبار

تحويل مصفوفة JSON من الكائنات إلى جمل SQL INSERT (مع CREATE TABLE اختيارياً).

قواعد البيانات

للتعمّق أكثر

تقدّمك في هذا الموضوع

جزء من مسار تعلم ضمان الجودة