SQLite / المتوسط: بنية الاستعلام وتصميم المعاملة / الدرس 9
الفهارس وفهارس التعبيرات والفهارس الجزئية
صمم فهارس SQLite من شروط محددة واحتياجات الخرج وكلفة الكتابة، مع تطبيق قواعد التعبيرات والفهارس الجزئية بدقة.
في هذه الصفحة
أهداف الدرس
- شرح ترتيب المفتاح المركب والوصول المغطى بالفهرس.
- استخدام تعبيرات حتمية متطابقة.
- تطبيق الفهرس الجزئي على مجموعة فرعية موثقة.
الفهرس بيانات تحتاج صيانة وليس تلميحاً مجانياً
يخزن الفهرس بنية وصول مرتبة قد تقلل العمل في البحث والفرز المناسبين. يستهلك مساحة ويحتاج صيانة مع الإضافة والتحديث والحذف المرتبطين به. قد يبطئ فهرس يفيد تقريراً نادراً مسار كتابة كثير التكرار. ابدأ بالشروط الفعلية وحجم النتيجة ومتطلبات الفرز وتواتر العمل، بدلاً من إضافة فهرس لكل عمود.
تحتوي الفهارس الثانوية في جداول rowid العادية معلومات موضع الصف؛ وتغير هوية INTEGER PRIMARY KEY وتخزين WITHOUT ROWID تفاصيل التصميم. لا تنقل مفهوم الفهرس العنقودي أو صياغة INCLUDE من SQL Server إلى SQLite. قد يوفر الفهرس المغطي القيم المطلوبة دون بحث إضافي في الجدول، لكن إضافة كل عمود معروض تزيد كلفة الصيانة والتخزين.
ضع المساواة والمجال في مفتاح مرشح
لمجال تواريخ خاص بعميل، يعد فهرس يبدأ بـcustomer_id ثم created_on مرشحاً معقولاً. قد تدعم الأعمدة اللاحقة الخرج أو كسر تعادل الفرز بحسب البنية الموثقة. ينشئ البرنامج الأول فهرساً صغيراً ويعيد طلباً معلوماً؛ فهو يتحقق من معنى الاستعلام لا من تحسن زمني عام. قد يكون مسح جدول صغير اختياراً صحيحاً.
تؤثر قواعد البادئة اليسرى والمتباينات والترتيب والانتقائية في الوصول المفيد. لا يخدم فهرس على(a,b) تلقائياً كل بحث على b كأنها المفتاح الأول. قد تستفيد استراتيجيات المحسن من معلومات إضافية، لكن ذلك سبب لفحص الدليل لا لوعد ببحث انتقائي. راجع الفهارس المتداخلة الموجودة قبل إضافة مرشح آخر.
CREATE TABLE purchase(id INTEGER PRIMARY KEY,customer_id INTEGER,created_on TEXT,amount INTEGER);
INSERT INTO purchase VALUES(1,1,'20260101',10),(2,1,'20260102',20),(3,2,'20260103',99);
CREATE INDEX purchase_customer_date ON purchase(customer_id,created_on,amount);
SELECT id,amount FROM purchase WHERE customer_id=1 AND created_on>='20260102'
ORDER BY created_on,id;
id|amount
2|20
يعيد الاستعلام الطلب2 بمبلغ20. لا يختلق المثال تحسناً زمنياً ولا يلزم المحسن اختيار خطة بعينها.
طابق التعبير والمجموعة المفهرسة
قد يدعم فهرس التعبير استعلاماً يستخدم التعبير المفهرس وفق قواعد المطابقة في SQLite. لا يجري المحسن عموماً جبراً اعتباطياً ليثبت تكافؤ تعبيرين مختلفين. التزم التعبير الموثق وراعِ قيوده، ومنها الدوال الحتمية. لا يحول فهرس lower(name) كل مقارنات النص إلى نظام عام لتطبيع Unicode.
يحتوي الفهرس الجزئي الصفوف المحققة لشرط WHERE فقط. يفرض البرنامج الثاني حساباً نشطاً واحداً لكل بريد، مع السماح بصف تاريخي غير نشط بالبريد نفسه. هذه قاعدة تفرد تجارية لا حيلة سرعة فقط. يجب أن يستلزم الاستعلام المجموعة المفهرسة وفق الاستدلال الذي يدعمه المحسن قبل تطبيق الفهرس بأمان.
CREATE TABLE account(id INTEGER PRIMARY KEY,name TEXT,email TEXT,active INTEGER NOT NULL CHECK(active IN(0,1)));
CREATE INDEX account_lower_name ON account(lower(name));
CREATE UNIQUE INDEX account_active_email ON account(email) WHERE active=1;
INSERT INTO account VALUES(1,'AYA','a@example.test',1),(2,'aya','a@example.test',0),(3,'Omar','b@example.test',1);
SELECT id,name FROM account WHERE lower(name)='aya' ORDER BY id;
SELECT email,COUNT(*) AS copies FROM account GROUP BY email HAVING COUNT(*)>1 ORDER BY email;
id|name
1|AYA
2|aya
email|copies
a@example.test|2
تطابق الصيغتان lower(name). يجوز تكرار البريد لأن صفاً واحداً فقط من صفوفه نشط.
أبقِ تعبير البحث متوافقاً مع التخزين
قد يمنع تطبيق دالة أو تحويل على عمود مفهرس مجال وصول مباشر مفيداً، ما لم يوجد فهرس تعبير مناسب مطابق. مع طوابع زمنية ذات ترميز متسق، قد يكشف مجال نصف مفتوح قيماً متجاورة دون استخراج الشهر من كل صف. تبقى أنواع المعاملات والقيم المخزنة مؤثرة وفق قواعد affinity. افحص SQL التي يرسلها التطبيق فعلياً، لا تقريباً يدوياً مختلفاً.
قد يجعل مجال واسع مطابق مسح الجدول مناسباً رغم وجود فهرس قابل للاستخدام. تحتاج تسميات SEARCH وSCAN وCOVERING سياقاً: كم صفاً يطابق، وأي قيم تلزم، وما العمل المتبقي؟ لا تستخدم INDEXED BY لفرض الفهرس كضبط روتيني؛ فهو عقد أقوى وله عواقب فشل عندما يتعذر استخدام الفهرس المحدد.
قس الموازنة الكاملة بين القراءة والكتابة
تحقق من تطابق مفاتيح النتائج وقيمها قبل مقارنة الخطط. قارن بعدها توزيعات ممثلة وتواتر الاستعلام وكلفة الكتابة وحجم القاعدة ومدة المعاملات المتزامنة في بيئة معزولة. يشرح المثال الاصطناعي الصغير البنية ولا يثبت انتقائية الإنتاج. سجل سياق إصدار التشغيل والإحصاءات، فقد تتغير اختيارات المحسن بالإصدار أو البيانات.
لا تحذف فهرساً قبل فحص دوره في فرض التفرد وأحمال العمل المعتمدة عليه. قد يفرض فهرسان متشابهان قواعد جزئية أو ترتيبات مختلفة. اجعل الترحيل قابلاً للتعافي بخطة مختبرة، واحتفظ بسبب وجود كل فهرس. التصميم الأفضل مجموعة صغيرة مبررة تخدم الثوابت ومسارات الوصول الفعلية، لا أكبر عدد من الفهارس الممكنة.
الأخطاء الشائعة وإصلاحها
لا يستخدم التعبير المفهرس كما توقع التطبيق.
- لماذا يحدث؟
- لا يطابق تعبير الاستعلام أو ترتيبه اللغوي عقد الفهرس.
- كيف تصلحه؟
- قارن التعبير الفعلي وافحص الخطة.
- كيف تتجنب تكراره؟
- اختبر SQL التطبيق وأنواع معاملاته.
ترفض قاعدة UNIQUE جزئية صفاً غير متوقع.
- لماذا يحدث؟
- ينتمي الصف إلى المجموعة المفهرسة ويتعارض داخلها.
- كيف تصلحه؟
- راجع المجموعة التجارية والمفتاح بدلاً من إزالة القاعدة عشوائياً.
- كيف تتجنب تكراره؟
- اختبر الانتقال بين الحالة النشطة وغير النشطة.
تباطأت الكتابة بعد إضافة فهارس كثيرة.
- لماذا يحدث؟
- يصون كل تغيير بنى إضافية.
- كيف تصلحه؟
- راجع المرشحين المتداخلين وقس موازنة حمل العمل.
- كيف تتجنب تكراره؟
- ضمّن ميزانية الكتابة والتخزين في مقترحات الفهارس.
اختبر فهمك
1. لماذا لا يضمن فهرس customer_id,created_on أفضل وصول لشرط created_on وحدها؟
اعرض الحل المشروح
تؤثر البادئة اليسرى وشكل الشرط الفعلي؛ إذ يرتب الفهرس حسب العميل أولاً. قد تفيد بدائل المحسن والإحصاءات حالات معينة، لكن يجب تقييم التصميم بخطة وحمل عمل فعليين. لا تفترض أن كل مكون في المفتاح المركب يعمل كفهرس مستقل عليه.
2. ما الذي تسمح به UNIQUE(email) WHERE active=1 وما الذي تمنعه؟
اعرض الحل المشروح
تسمح بصفوف غير نشطة تشترك في البريد، وتمنع صفين نشطين مفهرسين بمفتاح غير NULL متساوٍ وفق قواعد الفهرس. قرر قبول NULL منفصلاً. اختبر تنشيط صف مكرر غير نشط؛ فقد تخالف UPDATE القاعدة التجارية نفسها التي تخالفها INSERT.
أهم ما تعلمته
- للفهارس كلفة تخزين وتعديل.
- مطابقة التعبير والشرط الجزئي عقدان محددان.
- يجب أن تبرر الخطط والقياسات فائدة على مستوى حمل العمل.