SQLite / المتوسط: بنية الاستعلام وتصميم المعاملة / الدرس 12
دوال JSON وأنواع القيم المفردة وJSONB
استخدم JSON في SQLite بقصد، مع التمييز بين SQL NULL وJSON null والنصوص والمصفوفات، وتمثيل JSONB المعتم المشروط بالإصدار.
في هذه الصفحة
أهداف الدرس
- تحقق من شكل المستند ومعنى القيمة المفردة المستخرجة.
- ميّز المسار الغائب من JSON null الصريح.
- اشرح توافق JSONB وتوسيع البيانات بدوال جدولية.
لا يلغي JSON متطلبات النموذج العلائقي
يمثل JSON بيانات اختيارية أو حمولة متطورة، لكن المعرّفات الثابتة والعلاقات المطلوبة والحقول كثيرة القيود قد تلائم أعمدة عادية. حدد إصدار المستند والمسارات المطلوبة وأنواع القيم المفردة وحدود الحجم وسياسة الحقول المجهولة. قد يصح نحوياً ويخالف كل قواعد الأعمال. تفحص json_valid الصياغة بأعلامها المختارة، لا مخطط تطبيق كامل.
تُضمّن دوال JSON افتراضياً من SQLite 3.38.0، وقد يحذف البناء دعمها؛ فافحص المحرك المنشور. تدعم بيئة الدورة 3.45.1 الدوال المعروضة وJSONB الذي بدأ في 3.45.0. تحدد الحدود الميزات المختبرة، ولا تضمن وجودها في كل مكتبة هاتف أو إطار أقدم.
افحص نوع SQL المستخرج ووجود المسار
بمسار واحد قد تعيد json_extract عدداً صحيحاً أو حقيقياً أو نصاً أو NULL من SQL، لا سلسلة منسقة كـ JSON دائماً. وقد تتحول القيمة المنطقية إلى 1 أو 0. يستخرج النص الأول count بقيمة 2 وtrue بقيمة 1. يهم ذلك للمقارنة والربط والتجميع؛ فلا تفترض اتفاقه مع محرك آخر.
قد ينتج SQL NULL من JSON null صريح ومن مسار مفقود. تميز json_type بينهما: النص null للموجود وSQL NULL للغائب. وعلامة NULL الكبيرة في العرض تنسيق نتائج الدورة، لا رمز JSON المخزن. حدد ما يعني «لم يقدم» وما يعني إسناد null صريحاً.
CREATE TABLE event(id INTEGER PRIMARY KEY,payload TEXT NOT NULL CHECK(json_valid(payload))) STRICT;
INSERT INTO event VALUES(1,'{"count":2,"active":true,"optional":null}');
SELECT json_extract(payload,'$.count') AS count,
typeof(json_extract(payload,'$.count')) AS storage,
json_extract(payload,'$.active') AS active,
json_type(payload,'$.optional') AS present_null,
json_type(payload,'$.absent') AS missing FROM event ORDER BY id;
count|storage|active|present_null|missing
2|integer|1|null|NULL
تكون count عدداً صحيحاً في SQL بقيمة 2، وتتحول true إلى 1. تعيد json_type النص null للمسار الموجود وSQL NULL للمفقود.
أبقِ JSONB معتماً وراعِ توافق الإصدار
يخزن JSONB في SQLite تمثيلاً ثنائياً داخلياً كـ BLOB، وليس JSONB الخاص بـ PostgreSQL. لا تختلقه أو تهندسه عكسياً كصيغة نقل محمولة. استخدم الدوال الموثقة وتمثيل تبادل مقصوداً. قد يقلل كلفة التحليل أو التخزين، لكنه لا يضمن وصولاً ثابت الزمن لكل عملية أو فهرسة آلية لكل مسار.
ينشئ النص الثاني JSONB باستخدام jsonb()، ويتحقق منه بالعلم الصارم المختار 8، ويوسع مصفوفة عبر json_each. تبقى count المستخرجة 2، ويصير العنصران صفين مستقلين. يتطلب JSONB الدائم قراء متوافقين وترحيلات مدروسة؛ فنسخ ملف احتياطياً وحده لا يجعل مكتبة SQLite قديمة تفهم ميزة أحدث.
CREATE TABLE document(id INTEGER PRIMARY KEY,payload BLOB NOT NULL CHECK(json_valid(payload,8))) STRICT;
INSERT INTO document VALUES(1,jsonb('{"count":2,"tags":["x","y"]}'));
SELECT typeof(payload) AS storage,json_extract(payload,'$.count') AS count FROM document ORDER BY id;
SELECT d.id,j.key,j.value FROM document AS d, json_each(d.payload,'$.tags') AS j ORDER BY d.id,j.key;
storage|count
blob|2
id|key|value
1|0|x
1|1|y
نوع التخزين BLOB، وتبقى القيمة المستخرجة عدداً صحيحاً بقيمة 2. ينتج الموضعان 0 و1 القيمتين x وy بالترتيب الصريح.
افصل القيمة النصية عن قيمة JSON المركبة
تميز دوال إنشاء JSON وسيط قيمة SQL عاديّاً من قيمة أنتجتها دالة مناسبة بصيغة JSON. تمرير النص SQL الذي يحتوي [1,2] في موضع قيمة قد ينشئ سلسلة JSON لا مصفوفة؛ أما تغليفه بـ json() فيعبّر عن بنية JSON. تحقق من الشكل المتوقع وافحص json_type، ولا تعتمد على جزء مطبوع يبدو مألوفاً.
تحقق من المسارات والمفاتيح المختارة ديناميكياً، وأبقِ القيم العادية مرتبطة عبر واجهة البيئة المضيفة. لا يبرر JSON تجميع نص SQL بالربط. قد يكشف عمود مولد أو فهرس تعبير قيمة كثيرة الاستعلام وفق قواعد الحتمية والإصدار الموثقة، لكن الفهارس والقيود الإضافية تزيد كلفة الكتابة ويجب أن توافق تعبير الاستعلام بدقة.
تعامل بحذر مع التشوه وسلوك BLOB القديم
قد تثير دوال JSON خطأ عند محاولة تفسير نص مشوه. تحقق عند الحد الفاصل، وميّز فشل الصياغة والمسار والنوع والغياب. تغير سلوك JSON القديم داخل BLOB حول إصدارات 3.45 وله محاذير توافق موثقة؛ استخدم TEXT JSON المقصود أو JSONB المنشأ بالدوال بدلاً من تفسير BLOB عرضي. ولا تعتبر JSONB موضعاً لإدخال بايتات اعتباطية.
اختبر null الصريح والمفاتيح الغائبة والمصفوفات والأنواع والمدخل المشوه وتوافق الإصدار مع كل قارئ. احفظ المدخل الاصطناعي الأصلي أو المعتمد للتشخيص دون كشف حساس. يشرح عقد المستند القيم المستعلم عنها علائقياً وتطور المخطط، وما يحدث عند افتقاد القارئ قدرة JSON المطلوبة.
الأخطاء الشائعة وإصلاحها
malformed JSON
- لماذا يحدث؟
- تلقت دالة نص مستند غير صالح أو بايتات غير متوافقة مع التمثيل المقصود.
- كيف تصلحه؟
- تحقق من التمثيل المراد، وارفض المدخل غير الصالح أو اعزله.
- كيف تتجنب تكراره؟
- افحص الصياغة والحجم عند إدخال البيانات.
تُخزن مصفوفة مفترضة كسلسلة JSON.
- لماذا يحدث؟
- اختلطت قيمة نص SQL بمدخل JSON مركب.
- كيف تصلحه؟
- استخدم دالة إنتاج JSON الموثقة، وتحقق من json_type.
- كيف تتجنب تكراره؟
- اختبر الشكل المنشأ، لا النص المطبوع وحده.
يفشل قارئ أقدم عند التعامل مع JSONB.
- لماذا يحدث؟
- أُغفل توافق الميزات من عقد الملف.
- كيف تصلحه؟
- استخدم محركاً متوافقاً أو تصميماً صريحاً لترحيل التخزين والتبادل.
- كيف تتجنب تكراره؟
- سجل متطلبات ميزات JSON لكل تطبيق مستهلك.
اختبر فهمك
1. تعيد json_extract القيمة NULL لمسارين. كيف تميز JSON null الصريح من المفتاح الغائب؟
اعرض الحل المشروح
استخدم json_type لكل مسار. ينتج JSON null الموجود النص null، بينما ينتج المسار الغائب SQL NULL. قرر كيفية تمثيل الحالتين في عقد التطبيق، ولا تستبدلهما تلقائياً بالقيمة الافتراضية نفسها فتفقد الفرق الدلالي.
2. هل يضمن تخزين JSONB التوافق مع PostgreSQL أو الوصول ثابت الزمن؟
اعرض الحل المشروح
لا؛ إنه تمثيل SQLite المعتم الخاص، ويُعالج عبر واجهاته الموثقة. لا تعني خصائص كفاءته وصولاً عاماً بكلفة O(1)، ولا فهارس علائقية تلقائية. اختر سياسة التبادل وسياسة الفهرسة كلّاً على حدة.
أهم ما تعلمته
- التحقق من صياغة JSON ليس مخطط مجال كاملاً.
- تختلف SQL NULL وJSON null والمسارات الغائبة والقيم النصية.
- لـ JSONB حدود صريحة في الإصدار والتمثيل والتوافق بين الأنظمة.