حذف البيانات بأمان: DELETE
الحذف لا رجعة فيه؛ نتعلم كيف نحذف بدقة وأمان، ومتى لا نحذف. ⏱ نحو 25 دقيقة.
- تحذف صفوفًا محددة بـ
DELETE…WHERE. - تتبع خطوات الحذف الآمن بالمعاملة.
- تحذف مع احترام المفاتيح الأجنبية (الأبناء قبل الآباء).
- تفرّق بين
DELETEوTRUNCATEوDROP، وتطبّق الحذف الناعم.
١حذف الصفوف: DELETE
DELETE FROM يحذف الصفوف التي تحقق شرط WHERE. لا أعمدة في الأمر؛ لأنه يحذف الصف كاملًا:
DELETE FROM productsWHERE id = 4; SELECT COUNT(*) AS products FROM products;
| products |
|---|
| 11 |
-- حذف كل المنتجات التي نفدت ولم تُطلب قطDELETE FROM productsWHERE stock = 0 AND id NOT IN (SELECT product_id FROM order_items); SELECT id, name, stock FROM products WHERE stock = 0;
| id | name | stock |
|---|---|---|
| (لا توجد صفوف تحقق الشرط) | ||
٢القاعدة الأهم: WHERE
DELETE FROM order_items; -- بلا WHERE! SELECT COUNT(*) AS items_left FROM order_items;
| items_left |
|---|
| 0 |
حُذفت كل أصناف كل الطلبات. لا سلة محذوفات ولا «تراجع» بعد الاعتماد.
BEGIN;DELETE FROM customers WHERE city = 'جدة' AND id NOT IN (SELECT customer_id FROM orders);-- حُذف صف واحد كما توقعنا؟ نعتمدCOMMIT; SELECT name, city FROM customers WHERE city = 'جدة';
| name | city |
|---|---|
| ريم الشهري | جدة |
٣الحذف والمفاتيح الأجنبية
لا يُحذف صف له أبناء يشيرون إليه، إلا إن كان المفتاح الأجنبي ON DELETE CASCADE. في «متجر الواحة» لا CASCADE، فلحذف طلب نحذف أصنافه أولًا:
DELETE FROM orders WHERE id = 5;BEGIN;DELETE FROM order_items WHERE order_id = 5; -- الأبناء أولًاDELETE FROM orders WHERE id = 5; -- ثم الأبCOMMIT; SELECT COUNT(*) AS orders FROM orders;
| orders |
|---|
| 11 |
٤الحذف الناعم: لا تحذف، بل علِّم
في أغلب الأنظمة الحقيقية لا تُحذف البيانات المهمة فعلًا؛ لأننا نحتاجها للتقارير والمراجعة والقانون. بل نضع علامة أنها «محذوفة» ونخفيها من الشاشات. ويسمى هذا الحذف الناعم Soft Delete:
ALTER TABLE customers ADD COLUMN deleted_at TEXT; -- فارغ = نشط -- «حذف» العميلة لمىUPDATE customers SET deleted_at = '2025-06-10' WHERE id = 10; -- الشاشات تعرض النشطين فقطSELECT id, name FROM customers WHERE deleted_at IS NULL;
| id | name |
|---|---|
| 1 | سعود العتيبي |
| 2 | نورة القحطاني |
| 3 | خالد الحربي |
| 4 | ريم الشهري |
| 5 | فهد الدوسري |
| 6 | سارة المطيري |
| 7 | عبدالله الزهراني |
| 8 | هند السبيعي |
| 9 | ماجد الغامدي |
| الحذف الفعلي | الحذف الناعم | |
|---|---|---|
| الاسترجاع | من النسخة الاحتياطية فقط | بتعديل واحد: deleted_at = NULL |
| التقارير القديمة | تتغير أو تنكسر | سليمة |
| الثمن | — | كل استعلام يحتاج الشرط WHERE deleted_at IS NULL |
| متى؟ | بيانات مؤقتة، أو طلب قانوني بحذف بيانات شخص | العملاء والطلبات والفواتير |
٥DELETE و TRUNCATE و DROP
| الأمر | ماذا يحذف؟ | WHERE؟ | العائلة |
|---|---|---|---|
DELETE FROM t WHERE … | صفوفًا محددة | نعم | DML |
DELETE FROM t | كل الصفوف، ويبقى الجدول | لا | DML |
TRUNCATE TABLE t | كل الصفوف بسرعة، ويعيد الترقيم من 1 (MySQL) | لا | DDL |
DROP TABLE t | الجدول نفسه ببنيته وبياناته | لا | DDL |
TRUNCATE غير موجود في SQLite؛ مقابله DELETE FROM t;. وفي MySQL لا يعمل TRUNCATE على جدول تشير إليه مفاتيح أجنبية.
توقّع وتحقّق
حذف صفوف محددة:
DML.حذف الجدول نفسه:
الحذف الناعم يعني:
Soft Delete.لحذف طلب بلا CASCADE:
الخلاصة
DELETE FROM t WHEREشرط; وبلاWHEREتُحذف كل الصفوف.- اختبر بـ
SELECT، ثم احذف داخل معاملة، ثمCOMMITأوROLLBACK. - احذف الأبناء قبل الآباء، أو استخدم
CASCADEبوعي. - الحذف الناعم: عمود مثل
deleted_atبدل الحذف الفعلي. DELETEصفوف، وTRUNCATEكل الصفوف بسرعة، وDROPالجدول نفسه.
تحدٍّ برمجي: تنظيف الطلبات الملغاة
متوسطالإدارة قررت حذف الطلبات الملغاة نهائيًّا مع أصنافها، داخل معاملة.
- ابدأ معاملة.
- احذف أصناف كل الطلبات الملغاة من
order_items. - ثم احذف الطلبات الملغاة من
orders. - اعتمد المعاملة. لا تحذف أي شيء آخر.
👀 اعرض النتيجة المطلوبة
| COUNT(*) |
|---|
| 0 |
| id |
|---|
| 1 |
| 2 |
| 3 |
| 4 |
| 6 |
| 7 |
| 8 |
| 9 |
| 10 |
| 11 |
| 12 |
| order_id | product_id |
|---|---|
| 1 | 1 |
| 1 | 3 |
| 2 | 2 |
| 2 | 5 |
| 3 | 7 |
| 3 | 8 |
| 3 | 9 |
| 4 | 3 |
| 6 | 1 |
| 6 | 6 |
| 6 | 11 |
| 7 | 2 |
| 7 | 10 |
| 8 | 3 |
| 8 | 5 |
| 9 | 1 |
| 10 | 9 |
| 10 | 11 |
| 11 | 1 |
| 11 | 7 |
| 11 | 8 |
| 12 | 6 |
- الكود يعمل بلا أخطاء
- لا طلبات ملغاة
- بقية الطلبات سليمة
- بقية الأصناف سليمة
- داخل معاملة معتمدة
💡 تلميح 1
WHERE order_id IN (SELECT id FROM orders WHERE status = 'ملغى')💡 تلميح 2
حاول بنفسك أولًا؛ للمسألة أكثر من حلّ صحيح.
BEGIN; DELETE FROM order_itemsWHERE order_id IN (SELECT id FROM orders WHERE status = 'ملغى'); DELETE FROM ordersWHERE status = 'ملغى'; COMMIT;