Menu
flag Ar iconالعربيةdown icon

جملة RETURNING في SQLite: استرجاع الصفوف بعد INSERT و UPDATE

تعرّف على طريقة عمل جملة RETURNING في SQLite لاسترجاع الصفوف الناتجة عن INSERT أو UPDATE أو DELETE مباشرةً دون الحاجة إلى استعلام إضافي.

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

طريقة لمعرفة ما حدث للتو

عند تنفيذ INSERT أو UPDATE أو DELETE، يخبرك SQLite بعدد الصفوف التي تأثّرت، لكنه لا يكشف لك أيّ صفوف هي بالضبط، ولا كيف صارت قيمها النهائية. الحل التقليدي هو تشغيل استعلام SELECT بعدها مباشرة، وهذا يعني رحلتين إلى قاعدة البيانات وأمرين منفصلين، مع نافذة سباق صغيرة قد يعدّل فيها شخص آخر الصف بين الأمرين.

هنا يأتي دور جملة RETURNING في SQLite لحل المشكلة. تكتفي بإضافتها إلى نهاية أمر الكتابة، وتحدّد الأعمدة التي تريد استرجاعها، فيعيد لك SQLite الصفوف المتأثّرة وكأنك نفّذت SELECT عليها للتو:

عبارة واحدة، رحلة واحدة إلى قاعدة البيانات، وتسترجع الـ id المُولَّد تلقائياً مع قيمة created_at الافتراضية التي ملأتها قاعدة البيانات نيابةً عنك.

أُضيفت جملة RETURNING في إصدار SQLite 3.35.0 (مارس 2021). إذا رفض المحرّك الجملة باعتبارها خطأ في الصياغة، تحقّق من الإصدار عبر SELECT sqlite_version(); — فالإصدارات الأقدم ببساطة لا تعرف هذه الكلمة المفتاحية.

استرجاع الـ id بعد INSERT في SQLite

السبب الأشهر لاستخدام RETURNING هو الحصول على المفتاح الأساسي المُولَّد تلقائياً مباشرةً بعد عملية الإدراج:

قبل ظهور RETURNING، كانت الطريقة المعتادة هي تنفيذ INSERT ثم استدعاء last_insert_rowid() (أو ما يعادلها في المكتبة التي تستخدمها) على نفس الاتصال. هذه الطريقة لا تزال تعمل، لكنها تعتمد على حالة الاتصال نفسه — وهذا أمر يسهل أن يقع فيه خطأ عند استخدام تجمّعات الاتصالات (connection pools) أو عند العمل مع عدة خيوط (threads). أما RETURNING id فهو صريح ومرتبط بالجملة نفسها، ويعمل بالطريقة ذاتها بغضّ النظر عن البيئة التي يعيش فيها الاتصال.

وإذا لم يكن جدولك يُعرِّف عمودًا صريحًا من نوع INTEGER PRIMARY KEY، فلا يزال بإمكانك الحصول على معرّف الصف الضمني:

كل جدول عادي في SQLite له rowid، وجملة RETURNING ترجعه لك بكل بساطة.

أعمدة متعددة وتعبيرات داخل RETURNING

تقبل جملة RETURNING نفس الصيغة المتاحة في قائمة أعمدة SELECT. تقدر تكتب أسماء الأعمدة، أو تستخدم *، أو تبني تعبيرات وتعطيها أسماء بديلة (aliases):

RETURNING * مفيدة لما تبغى ترجع كل الأعمدة دفعة وحدة، بما فيها أي قيم افتراضية عبّتها قاعدة البيانات، دون ما تضطر تكتب اسم كل عمود:

سترى id الجديد، والـ name الذي مرّرته، والطابع الزمني الذي حسبه SQLite.

استخدام RETURNING مع UPDATE

عند تنفيذ UPDATE، تُعيد لك RETURNING القيم بعد التحديث — أي الصف بشكله الجديد بعد تطبيق التعديلات:

ستحصل على الرصيد الجديد لـ Ada وهو 125، وليس القديم 100. وهذا بالضبط ما يجعل RETURNING مثالياً للعدّادات الذرّية وعمليات الإيداع والسحب — فلستَ بحاجة لأن تقرأ ثم تحسب ثم تكتب ثم تعيد القراءة من جديد.

وإذا تطابق شرط WHERE مع عدة صفوف، فستحصل على صف مُعاد لكل صف متأثّر:

ثلاثة صفوف داخلة، ثلاثة صفوف خارجة. الترتيب غير مضمون — إذا كنت تحتاج ترتيبًا معيّنًا، رتّب النتيجة من جهة العميل.

استخدام RETURNING مع DELETE

عند تنفيذ DELETE، تُرجع لك جملة RETURNING الصفوف بحالتها قبل الحذف مباشرة. هذا مفيد للأرشفة، أو لتسجيل سجلات التدقيق، أو حتى لمجرد التأكد ممّا تم حذفه فعلًا:

تستعيد الجلستين المنتهيتين بكامل حقولهما، حتى لو لم تعودا موجودتين في الجدول. وإذا أردت نقلهما إلى مكان آخر، فهذه فرصة مثالية لجدول أرشيف — اقرأ النتيجة وأدخلها في جدول آخر داخل نفس المعاملة.

استخدام RETURNING مع UPSERT في SQLite

تعمل جملة RETURNING أيضاً مع INSERT ... ON CONFLICT ... DO UPDATE. والصف المُعاد يعكس الفرع الذي تم تنفيذه فعلياً — سواء كان إدخالاً جديداً أم تحديثاً ناتجاً عن التعارض:

شغّل هذه العبارة مرتين. في المرة الأولى يحدث الإدراج ويُعاد لك ('visits', 1). وفي المرة الثانية يقع التعارض فتُزاد القيمة، فتحصل على ('visits', 2). في كلتا الحالتين: عبارة واحدة وصف واحد عائد — دون الحاجة إلى السؤال "هل تم الإدراج أم التحديث؟" قبل المتابعة.

هذا أنظف نمط في SQLite لتنفيذ فكرة "أعطني القيمة الحالية، وأنشئها إن لم تكن موجودة" بدون ذهاب وإياب إلى قاعدة البيانات.

بعض الأمور التي ينبغي معرفتها

هناك تفاصيل صغيرة قد تربك المطورين عند التعامل مع جملة RETURNING في SQLite:

  • RETURNING تُرجع دائماً حالة الصف بعد التغيير في حالتي INSERT وUPDATE، و_قبل_ التغيير في حالة DELETE. لا توجد صياغة لطلب الجهة الأخرى.
  • ترتيب الصفوف المُعادة غير مضمون. أضف ORDER BY من جهة العميل إذا كان الترتيب مهماً.
  • لا يمكنك وضع RETURNING داخل استعلام فرعي. فهي عبارة عُليا تابعة لجملة الكتابة، وليست تعبيراً يمكن تضمينه.
  • RETURNING لا تعكس البيانات المعدَّلة من مشغّلات BEFORE بحد ذاتها — بل تُرجع القيم التي كُتبت فعلياً. أما مشغّلات AFTER فتعمل بين عملية الكتابة وإرجاع الصف.
  • الأعمدة المُولَّدة (Generated columns) والقيم الافتراضية DEFAULT تظهر في النتيجة. ولهذا تحديداً يُعدّ RETURNING * طريقة سريعة لمعاينة ما ملأته قاعدة البيانات نيابةً عنك.

التالي: استيراد بيانات CSV

RETURNING ممتازة عندما تكتب صفاً واحداً أو بضعة صفوف وتريد رؤية النتيجة فوراً. أما حين تحتاج إلى تحميل آلاف الصفوف من ملف، فستلجأ إلى أدوات استيراد CSV في SQLite — وهذا موضوع الصفحة التالية.

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

هل تدعم SQLite جملة RETURNING؟

نعم، الدعم متوفر منذ الإصدار 3.35.0 الصادر في مارس 2021. يمكنك إلحاق RETURNING بجمل INSERT و UPDATE و DELETE للحصول على الصفوف المتأثرة. أما إذا كنت تستخدم إصدارًا أقدم فسيرفض المحلل اللغوي الجملة، لذا تحقّق من الإصدار عبر SELECT sqlite_version();.

كيف أحصل على معرّف الصف الذي أدرجته للتو في SQLite؟

استخدم INSERT ... RETURNING id (أو RETURNING rowid إذا لم يكن للجدول مفتاح أساسي صريح). ستحصل على القيمة المُولّدة ضمن نفس الجملة، فلا حاجة إلى استعلام ثانٍ مثل last_insert_rowid().

هل يمكن لـ RETURNING إرجاع أكثر من عمود؟

نعم، اكتب الأعمدة المطلوبة مفصولة بفواصل تمامًا كما في SELECT، مثل: RETURNING id, name, created_at. كما يمكنك استخدام RETURNING * لاسترجاع جميع الأعمدة، أو كتابة تعابير حسابية مثل RETURNING id, price * quantity AS total.

هل تعمل RETURNING مع UPSERT و ON CONFLICT؟

نعم، الجملة INSERT ... ON CONFLICT ... DO UPDATE ... RETURNING ... تُرجع الصف سواء كان قد أُدرج حديثًا أو حُدّث نتيجة لحل التعارض. وهذا يجعلها الطريقة الأنظف لتنفيذ UPSERT وقراءة النتيجة في رحلة واحدة إلى قاعدة البيانات.

Coddy programming languages illustration

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

ابدأ الآن