إدراج البيانات في الجداول باستخدام INSERT
تُستخدم جملة INSERT لإضافة صفوف جديدة إلى الجدول في SQLite. الصيغة بسيطة وسهلة الحفظ:
ثلاثة أجزاء يجب الانتباه إليها:
INSERT INTO books— الجدول المستهدف.(title, author, year)— الأعمدة التي ستُدرج لها القيم.VALUES (...)— القيم، بنفس ترتيب قائمة الأعمدة.
لاحظ أن id غير موجود في قائمة الأعمدة، لذا يتولى SQLite توليده تلقائيًا (لأنه من نوع INTEGER PRIMARY KEY، فيأخذ قيمة rowid). وأي عمود تتجاهله سيأخذ قيمته الافتراضية، أو NULL إن لم تكن له قيمة افتراضية.
اذكر أسماء الأعمدة دائمًا
من الناحية التقنية يمكنك حذف قائمة الأعمدة وتمرير القيم لكل الأعمدة بترتيب تعريفها في الجدول:
-- يعمل، لكنه هشّ:
INSERT INTO books VALUES (NULL, 'Dune', 'Frank Herbert', 1965);
لا تفعل ذلك. بمجرد أن يضيف أحدهم عمودًا إلى books، ستنكسر كل تعليمة من هذا النوع، أو الأسوأ من ذلك أن تبدأ القيم في الذهاب إلى الأعمدة الخاطئة. اكتب أسماء الأعمدة صراحةً:
إنّ كتابة أسماء الأعمدة بشكل صريح هي نوع من التوثيق — فهي تجعل عبارة الإدراج مفهومة بحد ذاتها، دون الحاجة للعودة إلى تعريف الجدول للبحث عن الأعمدة.
إدراج صفوف متعددة في SQLite
يمكنك إدراج عدة صفوف دفعة واحدة في عبارة INSERT واحدة، وذلك بسرد أكثر من مجموعة قيم:
هذي الطريقة أنظف بكثير من كتابة ثلاث جمل INSERT منفصلة، و SQLite يتعامل معها كأنها جملة واحدة. لكن المكسب الحقيقي في الأداء عند إدراج بيانات ضخمة في sqlite يجي من تغليف عمليات الإدراج داخل معاملة (transaction) — وهذا التي بنشرحه بعد قليل.
إدراج صفوف متعددة في sqlite: غلّفها داخل معاملة
افتراضيًا، كل جملة INSERT تُعتبر معاملة مستقلة بذاتها. و SQLite يعمل fsync في نهاية كل واحدة منها، وهذا السبب الفعلي وراء بطء الحلقات الساذجة — مو عملية الإدراج نفسها.
اجمعها سوا:
عملية fsync واحدة بدل خمس. مع آلاف الصفوف، الفرق ممكن يوصل لمرتين أو ثلاث مراتب عشرية. ولو حصل أي خطأ في النص، فإن ROLLBACK يتراجع عن الدفعة كاملة.
هذا النمط هو الوصفة المعتمدة لـ sqlite bulk insert. سواء كنت تتعامل مع SQLite من Python أو Node أو Rust، غلّف الحلقة دائماً بـ BEGIN / COMMIT.
INSERT ... SELECT: النسخ من جدول آخر
تقدر تملأ جدولاً من نتيجة استعلام بدل القيم الحرفية:
الأعمدة في جملة SELECT تُطابَق حسب ترتيبها مع قائمة الأعمدة في INSERT، فالترتيب هو ما يهم لا الأسماء. هذه هي الطريقة المعتادة لأرشفة الصفوف، أو بناء جداول التقارير، أو نسخ جزء من البيانات أثناء عمليات الترحيل (migration).
القيم الافتراضية DEFAULT والأعمدة المُهمَلة
إذا كان للعمود قيمة DEFAULT معرَّفة، يمكنك حذفه من قائمة الأعمدة وسيقوم SQLite بملئه بالقيمة الافتراضية تلقائيًا:
تأخذ created_at الطابع الزمني الحالي تلقائيًا لأننا لم نُمرّر قيمة لها. وإذا أردت إدراج صف يعتمد كليًا على القيم الافتراضية — وهو أمر مفيد لإنشاء صفوف مبدئية — فاستخدم الصيغة DEFAULT VALUES:
صفّان جديدان، كلاهما بقيمة value = 0 ومعرّفات تُسنَد تلقائيًا.
INSERT OR IGNORE: تجاوز التكرارات
عندما يؤدي إدراج صف ما إلى انتهاك قيد UNIQUE أو PRIMARY KEY، فإن السلوك الافتراضي هو إيقاف التنفيذ مع إظهار رسالة خطأ:
خطأ: فشل قيد UNIQUE: users.email
INSERT OR IGNORE تستبدل ذلك بـ "تجاهل الصف المخالف بصمت":
تبقّت ثلاثة صفوف، وتمّ تجاهل الصف المكرر دون أي خطأ. هذه هي الطريقة المُثلى في SQLite للتعبير عن "أدرج إذا لم يكن موجودًا" مع البيانات التمهيدية البسيطة، فلا حاجة إلى SELECT منفصل للتحقق مسبقًا، ولا إلى التعامل مع الاستثناءات.
INSERT OR REPLACE: استبدال الصفوف المكررة
تقوم INSERT OR REPLACE بحذف الصف المتعارض وإدراج الصف الجديد مكانه:
انتبه لأمر مهم: REPLACE يعني DELETE ثم INSERT، وليس UPDATE. فلو كان الصف المحذوف مرتبطاً بمفاتيح خارجية تشير إليه مع ON DELETE CASCADE، فإن الصفوف الأبناء ستُحذف هي الأخرى. كما أن أي عمود لا تذكره في عملية INSERT الجديدة سيعود إلى قيمته الافتراضية — ولن يحتفظ بقيمته القديمة.
في معظم حالات «حدّث إن وُجد، وأدرج إن لم يوجد»، ما تحتاجه فعلياً هو عملية upsert حقيقية باستخدام ON CONFLICT ... DO UPDATE. وهذا موضوع له صفحة مستقلة.
مراجعة سريعة
INSERT INTO table (cols) VALUES (...)— الصيغة الأساسية. اذكر أسماء الأعمدة دائماً.- لإدراج صفوف متعددة في sqlite، استخدم مجموعات قيم مفصولة بفواصل بعد
VALUES. - للتحميل بالجملة (bulk insert) فعلياً، ضع عمليات الإدراج داخل
BEGIN/COMMIT. INSERT INTO ... SELECT ...يَنسخ الصفوف الناتجة عن استعلام.DEFAULT VALUESيُنشئ صفاً اعتماداً على القيم الافتراضية وحدها؛ والأعمدة التي تتجاهلها تأخذ قيمها الافتراضية أيضاً.INSERT OR IGNOREيتخطى الصفوف المتعارضة، بينماINSERT OR REPLACEيستبدلها (عبر حذف ثم إدراج).
ما التالي: UPDATE
إدراج البيانات في sqlite ليس سوى نصف القصة. النصف الآخر هو تعديل الصفوف الموجودة فعلاً — كزيادة عدّاد، أو تصحيح خطأ إملائي، أو تحديث حالة طلب إلى «تم الشحن». هذا ما تفعله جملة UPDATE، ولها عاداتها الخاصة التي يُستحسن إتقانها (خصوصاً جملة WHERE). نتناولها في الدرس القادم.
الأسئلة الشائعة
كيف أُدرج صفًا جديدًا في SQLite؟
استخدم الصيغة INSERT INTO table (col1, col2) VALUES (val1, val2);. ذكر أسماء الأعمدة اختياري لكنه مُستحسن جدًا، لأنه يضمن استمرار عمل الجملة حتى لو أُضيف عمود جديد للجدول لاحقًا. أما إذا حذفت قائمة الأعمدة، فيجب عليك تمرير قيمة لكل عمود وبنفس ترتيب التعريف.
كيف أُدرج عدة صفوف دفعة واحدة في SQLite؟
ضع عدة مجموعات بين أقواس بعد VALUES مفصولة بفواصل، هكذا: INSERT INTO t (a, b) VALUES (1, 2), (3, 4), (5, 6);. لكن إذا كنت تُدخل آلاف الصفوف فعلًا، فلفّ عمليات الإدراج داخل معاملة واحدة باستخدام BEGIN و COMMIT — هذا هو السر الحقيقي للسرعة، وليس صيغة الإدراج المتعدد بحد ذاتها.
ما وظيفة INSERT OR IGNORE في SQLite؟
INSERT OR IGNORE يتجاهل الصفوف التي ستُسبب مخالفة لقيد UNIQUE أو PRIMARY KEY أو NOT NULL بدلًا من إطلاق خطأ. الصف المتعارض يُتجاهل بصمت وتكمل بقية الجملة عملها بشكل طبيعي. استخدمه عندما تريد منطق «أدرج إن لم يكن موجودًا» دون الحاجة للتحقق المسبق من وجود الصف.
لماذا يظهر لي خطأ 'UNIQUE constraint failed' عند الإدراج؟
السبب أن SQLite عثر على صف موجود مسبقًا بنفس القيمة في عمود مُعرَّف كـ UNIQUE أو PRIMARY KEY. إما أن القيمة مكررة فعلًا، أو أنك تُعيد تشغيل سكربت إدخال البيانات الأولية. الحل: استخدم INSERT OR IGNORE لتخطي المكرر، أو INSERT OR REPLACE للكتابة فوقه، أو ON CONFLICT ... DO UPDATE (upsert) للتحكم الدقيق في السلوك.