Menu
flag Ar iconالعربيةdown icon
جرّب في Playground

أنواع البيانات في SQLite: التخزين والكتابة الديناميكية

كيف تخزّن SQLite القيم فعليًا؟ شرح فئات التخزين الخمس، ولماذا تُعتبر الكتابة في SQLite ديناميكية، وأهم المفاجآت التي تواجه القادمين من MySQL أو Postgres.

تحتوي هذه الصفحة على محررات قابلة للتشغيل — حرّر، شغّل، وشاهد النتيجة فوراً.

خمس فئات تخزين فقط، لا أنواع كثيرة

يخزّن SQLite كل قيمة باعتبارها واحدة من خمس فئات تخزين (storage classes):

  • NULL — أي غياب القيمة.
  • INTEGER — عدد صحيح بإشارة، يأخذ من 1 إلى 8 بايت حسب حجمه.
  • REAL — رقم عشري بصيغة IEEE بحجم 8 بايت.
  • TEXT — نص يُخزَّن بترميز قاعدة البيانات (غالباً UTF-8).
  • BLOB — بايتات خام تُحفَظ كما هي تماماً دون أي تعديل.

هذا كل شيء. لا يوجد نوع BOOLEAN مستقل، ولا DATETIME، ولا VARCHAR، ولا DECIMAL. قواعد البيانات الأخرى تقدّم عشرات الأنواع، أما SQLite فيكتفي بخمس فقط، وكل ما عداها مبني فوقها.

typeof() يكشف لك فئة التخزين الفعلية لكل قيمة، فستظهر لك القيم: integer, real, text, blob. هذه الفئات الأربع، إضافةً إلى null، هي كل ما تعرفه SQLite.

الكتابة الديناميكية في SQLite

هنا تأتي النقطة التي تُفاجئ القادمين من Postgres أو MySQL. في SQLite (بدون استخدام STRICT)، فإن النوع الذي تُعلن عنه عند تعريف العمود هو مجرد اقتراح لا أكثر، وليس عقداً ملزماً. النوع الحقيقي ملتصق بكل قيمة على حدة:

كلا الصفّين تم قبولهما. عمود id يحتوي على عدد صحيح في صفّ ونص في الآخر، وعمود body يحوي نصًا وعددًا صحيحًا. ببساطة، SQLite يقبل تخزين قيم من أي فئة في أي عمود.

هذه هي الكتابة الديناميكية (Dynamic Typing) في SQLite، وهي خيار تصميمي مقصود. تجعل التعامل مع SQLite مرنًا جدًا أثناء النماذج الأولية والسكربتات السريعة، لكنها في المقابل تعني أن خطأً مطبعيًا بسيطًا في كود تطبيقك قد يخزّن بيانات بالشكل الخاطئ لسنوات دون أن تلاحظ. إذا كان هذا التنازل يزعجك — وفي معظم مخططات الإنتاج ينبغي أن يزعجك فعلًا — فإن جداول STRICT هي الحل. سنصل إليها بعد قليل.

ميل النوع (Type Affinity) باختصار

النوع المُعلن للعمود ليس مُهمَلًا؛ بل يمنح العمود ما يُسمى بـ_ميل النوع_ (Affinity). عند إدراج قيمة، يحاول SQLite تحويلها بما يتوافق مع ميل العمود طالما أن التحويل ممكن دون فقدان للبيانات. فعمود من نوع TEXT يستقبل الرقم 42 سيخزّنه كنص '42'، وعمود INTEGER يستقبل النص '42' سيخزّنه كعدد صحيح 42. أما إذا كان التحويل سيؤدي إلى فقدان معلومات، فإن SQLite يحتفظ بالنوع الأصلي كما هو.

الصف الأول: العدد الصحيح 42 تحوَّل إلى نص '42'، والنص '100' تحوَّل إلى عدد صحيح 100. الصف الثاني: القيمة '3.5' لا يمكن تحويلها إلى INTEGER دون فقدان، فبقيت كنص. سنُفرد لاحقًا صفحة كاملة لشرح الـ Affinity، لكن يكفي الآن أن تعرف أن نوع العمود يؤثر على طريقة التخزين رغم أنه لا يفرضها.

نوع Boolean في SQLite

لا توجد فئة تخزين باسم BOOLEAN في SQLite. القيم المنطقية تُخزَّن كأعداد صحيحة: 0 تعني false و 1 تعني true:

الكلمتان TRUE وFALSE معروفتان في SQLite (منذ الإصدار 3.23) وتُترجَمان إلى 1 و0. تعريف العمود بالنوع BOOLEAN يمنحه ميلًا رقميًا (numeric affinity)، لكنه لا يحصره في القيمتين 0 و1 فقط؛ فبدون وضع STRICT، يمكنك إدراج قيمة مثل 'maybe' ولن تعترض SQLite على ذلك.

تخزين التاريخ والوقت في SQLite

لا يوجد كذلك نوع باسم DATETIME. أمامك ثلاث طرق للترميز، ودوال التاريخ في SQLite تتعامل معها جميعًا:

  • TEXT بصيغة ISO-8601، مثل: '2026-04-23 14:30:00'.
  • REAL على هيئة أرقام اليوم اليولياني (Julian day).
  • INTEGER بعدد الثواني منذ بداية حقبة Unix.

النص بصيغة ISO-8601 هو الخيار الأكثر شيوعاً — فهو يُرتَّب بشكل صحيح كسلسلة نصية، ومقروء للبشر، وكل الدوال المدمجة (date() وtime() وdatetime() وstrftime() وjulianday()) تتعامل معه. اختر صيغة واحدة لكل عمود والتزم بها؛ فخلط الصيغ داخل عمود واحد من الأخطاء التي تكتشفها بعد ستة أشهر وتندم عليها.

VARCHAR و CHAR وأسماء مألوفة أخرى

يقبل SQLite أسماء الأنواع التي تعرفها من قواعد بيانات أخرى — VARCHAR(255) وCHAR(10) وNVARCHAR وDECIMAL(10,2) وDOUBLE وFLOAT وINT وBIGINT وMEDIUMINT. كلها تُحلَّل بدون مشاكل، لكنها في النهاية تُختزَل إلى واحدة من فئات التخزين الخمس وفقاً لقواعد التقارب (affinity).

VARCHAR(255) لا يفرض فعليًا حدًّا أقصاه 255 حرفًا، فـ SQLite يتجاهل هذا الطول تمامًا. وكذلك DECIMAL(10, 2) لا يخزّن رقمًا عشريًا بدقة ثابتة، بل يأخذ صِفة (Affinity) عددية ويُحفظ داخليًا كـ INTEGER أو REAL. هذه التسميات موجودة لسبب واحد فقط: أن تعمل المخططات (Schemas) المنسوخة من قواعد بيانات أخرى دون أخطاء، لكنها لا تجلب معها القيود التي توحي بها أسماؤها في تلك الأنظمة.

إذا كنت تحتاج إلى حسابات عشرية دقيقة للأموال، فخزّن القيمة بالقروش (أو السنتات) كـ INTEGER. أمّا الأرقام العشرية من نوع REAL فسوف تُدخل أخطاء تقريب عند الخانة العشرية الثالثة عاجلًا أم آجلًا.

NULL فئة تخزين مستقلة

NULL ليس مجرّد "غياب قيمة"، بل هو قيمة لها فئة تخزين خاصة بها، وتُرجعها الدالة typeof():

b تظهر قيمته على أنها null. هذه نقطة مهمة، لأن NULL لا يساوي أي شيء، حتى ولو قارنته بـ NULL آخر. الشرط b = NULL لن يتحقق أبدًا، فلا بد أن تكتبه بالشكل b IS NULL. سنتعمق في هذه النقطة في صفحة المعاملات و NULL لاحقًا، لكن أصلها يبدأ من هنا، من فئة التخزين نفسها.

تخزين البيانات الثنائية باستخدام BLOB

النوع BLOB يخزّن البايتات الخام كما هي بدون أي تعديل، وهو مفيد للصور الصغيرة، وقيم التجزئة (hashes)، والبيانات المشفّرة، وأي شيء ليس نصًا ولا رقمًا:

تتيح لك الصياغة x'...' كتابة قيم BLOB بالنظام الست عشري داخل SQL، أما من داخل الكود البرمجي فستمرّر عادةً مصفوفة بايتات عبر مُعامل (parameter). الدالة length() على قيمة BLOB تُعيد عدد البايتات لا عدد الحروف.

ملاحظة عملية مهمة: SQLite يخزّن قيم BLOB الكبيرة بلا مشاكل، لكن سحب blob بحجم 50 ميجابايت مع كل استعلام يلمس الصف عملية بطيئة. مع الملفات الكبيرة، الأفضل أن تحفظ الملف على القرص وتُبقي مساره فقط داخل قاعدة البيانات.

خلاصة ما تعلّمناه

  • خمس فئات تخزين في SQLite — NULL وINTEGER وREAL وTEXT وBLOB — تكفي لتغطية كل شيء.
  • القيم المنطقية (Boolean) في SQLite تُخزَّن كأعداد صحيحة، أما التواريخ فلك حرية تخزينها كنص أو رقم عشري أو عدد صحيح.
  • أنواع الأعمدة المُعلَنة مجرد تلميحات، لا قيود مُلزِمة (إلا إذا استخدمت جداول STRICT).
  • صياغات مثل VARCHAR(255) تُحلَّل بنجاح، لكنها لا تفرض الطول أو الدقة التي تُوحي بها في أنظمة قواعد بيانات أخرى.
  • الدالة typeof(value) خير صديق لك كلما شككت في النوع الفعلي المُخزَّن.

التالي: تقارب الأنواع (Type Affinity)

سلوك "التلميح" الذي مررنا عليه سريعًا تحكمه قواعد دقيقة — خمس فئات تقارب (affinity) تُستنتج من اسم النوع المُعلَن، وتُطبَّق في كل عملية إدراج. هذا موضوع الصفحة التالية، وهو المفتاح للتنبؤ بما سيفعله SQLite فعليًا بالقيم التي تُمرّرها إليه.

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

ما هي أنواع البيانات التي تدعمها SQLite؟

تعتمد SQLite على خمس فئات تخزين فقط: NULL وINTEGER وREAL وTEXT وBLOB، وكل قيمة في قاعدة البيانات تُخزَّن وفق إحداها. أما الأسماء المألوفة مثل VARCHAR(255) وDATETIME وBOOLEAN فهي مقبولة داخل CREATE TABLE لأغراض التوافق فقط، لكنها في النهاية تُترجم إلى واحدة من الفئات الخمس عند التخزين.

هل يوجد في SQLite نوع منطقي (Boolean) أو نوع تاريخ ووقت (DateTime)؟

لا، ليس كفئات تخزين مستقلة. القيم المنطقية تُخزَّن كـ INTEGER (0 و1)، رغم أن SQLite تتعرّف أيضًا على الكلمتين TRUE وFALSE. أما التواريخ والأوقات فيمكن تخزينها كـ TEXT (سلاسل بصيغة ISO-8601)، أو كـ REAL (أرقام يوم جوليان)، أو كـ INTEGER (ثواني Unix epoch) — أنت من يختار الترميز، ودوال التاريخ في SQLite تعمل مع الصيغ الثلاث.

لماذا يُقال إن الكتابة في SQLite ديناميكية؟

في معظم قواعد البيانات، إذا أعلنتَ عمودًا بنوع INTEGER فلن يقبل أي نص. أما في SQLite (افتراضيًا) فالنوع المُعلَن مجرد تلميح، والنوع الفعلي يُحفظ مع كل قيمة على حدة، أي أن عمودًا من نوع TEXT يستطيع أن يحتوي عددًا صحيحًا إذا أدخلته بهذه الصورة. هذه المرونة قد تكون مفيدة أحيانًا، لكنها سلاح ذو حدّين. ولإيقافها يمكنك استخدام جداول STRICT.

Coddy programming languages illustration

تعلّم البرمجة مع Coddy

ابدأ الآن