أساسيات قواعد البيانات
قاعدة البيانات (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_dateDATE | wallet_balanceNUMERIC(10,2) | |
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 |
- الصفعميل واحد، في كل عمود منه قيمة أو 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، تحفظ العُقد والعلاقات بينها، مثل من يتابع من.
أخطاء شائعة
- اعتبار الشاشة دليلاً على ما خُزِّن؛ فقد تعيد الصفحة عرض ما كتبته بينما يحمل الصف قيمة مقطوعة أو مشوّهة.
- البحث عن سجل جديد بأخذ «آخر صف» بدلاً من البحث عنه بمفتاحه.
- معاملة
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- صنّف كل عمود: رقم تحسب به، أم رمز من أرقام، أم نص، أم تاريخ، أم «نعم أو لا».
- اختر نوعاً لكل عمود، مع الطول أو الدقة حيث يلزم، وحدّد هل هو إلزامي أم اختياري.
- اختر العمود الذي يحدد الصف، ثم اكتب فكرة اختبار واحدة لكل عمود مع نتيجتها المتوقعة.
إظهار الإجابة النموذجية
تصميم معقول بأنواع 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 أفضل من قاعدة البيانات العلائقية؟
ليس أحدهما أفضل من الآخر بإطلاق. فلكل منهما مفاضلات مختلفة في البنية والاستعلام والتوسّع، ومنتجات كثيرة تستخدم الاثنين. وبصفتك مختبِراً، اعرف أي مخزن يحفظ أي بيانات، وأي القواعد يفرضها كل مخزن بنفسه، وأيها يتركه للتطبيق.
المراجع
- ISO/IEC 9075-1:2023 — Information technology — Database languages SQL — Part 1: Framework (SQL/Framework)الجزء الإطاري من معيار SQL الدولي، الذي تنفّذه لهجات PostgreSQL وMySQL وSQLite وغيرها بدرجات متفاوتة.
- PostgreSQL Documentation: SQL Syntaxالبنية المعجمية لـ SQL في PostgreSQL: المعرّفات والاقتباس والثوابت النصية والتهريب.
- SQL As Understood By SQLiteقواعد SQL التي يقبلها SQLite، عبارة تلو الأخرى، مع مخططات نحوية.
تدرّب بأدوات متقن
تحويل بيانات CSV إلى جمل CREATE TABLE وINSERT مع استنتاج أنواع الأعمدة.
ولّد سكربتات تهيئة SQL (جملة CREATE TABLE وجمل INSERT) بصفوف اختبار واقعية لأي لهجة.
ولّد سلاسل نصية اختبارية بترميز Unicode عبر الخطوط (العربية والديفاناغارية وCJK والسيريلية) والرموز التعبيرية وعلامات الدمج وصيغ التطبيع.
قِس طول السلسلة النصية بالأحرف ونقاط الترميز (code points) وبايتات UTF-8 ووحدات UTF-16.
ولّد القيم الحدّية حول الحدود: min-1 وmin وmin+1 وmax-1 وmax وmax+1 للأعداد الصحيحة بأي عرض.
تحويل مصفوفة JSON من الكائنات إلى جمل SQL INSERT (مع CREATE TABLE اختيارياً).
للتعمّق أكثر
تقدّمك في هذا الموضوع
سجّل الدخول لتتابع تقدّمك
علّم الموضوعات مكتملة، وتابع من حيث توقفت، وافتح امتحانات التخصص.