أساسيات PHP

SQL: تحديث وحذف البيانات

13 دقيقة الدرس 33 من 45

تحديث البيانات باستخدام UPDATE

عبارة UPDATE تعدل السجلات الموجودة في جدول. كن حذراً جداً مع UPDATE - بدون شرط WHERE المناسب، ستقوم بتحديث جميع الصفوف!

الصيغة الأساسية

UPDATE table_name
SET column1 = value1, column2 = value2, ...
WHERE condition;

تحذير حاسم

قم دائماً بتضمين شرط WHERE إلا إذا كنت تريد عمداً تحديث جميع الصفوف. بدون WHERE، سيتم تحديث كل صف في الجدول!

-- خطير: يحدث جميع المنتجات!
UPDATE products SET price = 0;

-- آمن: يحدث منتج واحد محدد فقط
UPDATE products SET price = 99.99 WHERE id = 5;

مثال: تحديث عمود واحد

-- تحديث البريد الإلكتروني للمستخدم
UPDATE users
SET email = 'newemail@example.com'
WHERE id = 10;

مثال: تحديث أعمدة متعددة

-- تحديث سعر ومخزون المنتج
UPDATE products
SET price = 149.99, stock = 25
WHERE id = 3;

مثال: التحديث بناءً على شرط

-- إعطاء خصم 10% لجميع الإلكترونيات
UPDATE products
SET price = price * 0.9
WHERE category = 'Electronics';

-- وضع علامة على جميع المنتجات غير المتوفرة على أنها غير نشطة
UPDATE products
SET is_active = 0
WHERE stock = 0;

التحديث مع الحسابات

-- زيادة جميع الأسعار بنسبة 5%
UPDATE products
SET price = price * 1.05
WHERE category = 'Electronics';

-- إضافة 10 وحدات إلى المخزون
UPDATE products
SET stock = stock + 10
WHERE id = 15;

-- زيادة عدد المشاهدات
UPDATE posts
SET views = views + 1
WHERE id = 42;

تحديث صفوف متعددة

-- تحديث جميع المنتجات في فئات محددة
UPDATE products
SET is_active = 1
WHERE category IN ('Electronics', 'Books', 'Furniture');

-- وضع علامة على الطلبات القديمة على أنها مؤرشفة
UPDATE orders
SET status = 'archived'
WHERE created_at < DATE_SUB(NOW(), INTERVAL 1 YEAR);

التحديث مع عبارة CASE

تطبيق تحديثات مختلفة بناءً على الشروط:

-- تطبيق خصومات مختلفة بناءً على نطاق السعر
UPDATE products
SET price = CASE
    WHEN price > 500 THEN price * 0.80    -- خصم 20% للعناصر الباهظة
    WHEN price > 100 THEN price * 0.90    -- خصم 10% للمتوسطة
    ELSE price * 0.95                      -- خصم 5% للأرخص
END
WHERE category = 'Electronics';

التحديث مع LIMIT

-- تحديث أول 10 صفوف مطابقة فقط
UPDATE products
SET is_featured = 1
WHERE category = 'Electronics'
ORDER BY price DESC
LIMIT 10;

فحص التحديثات قبل التنفيذ

أفضل ممارسة: اختبر باستخدام SELECT أولاً

قبل تشغيل UPDATE، قم بتشغيل SELECT بنفس شرط WHERE لرؤية الصفوف التي ستتأثر:

-- 1. أولاً، تحقق من الصفوف التي ستتأثر
SELECT * FROM products
WHERE category = 'Electronics' AND price < 50;

-- 2. إذا كانت النتائج صحيحة، قم بتشغيل التحديث
UPDATE products
SET is_active = 0
WHERE category = 'Electronics' AND price < 50;

-- 3. تحقق من التحديث
SELECT * FROM products
WHERE category = 'Electronics' AND price < 50;

حذف البيانات باستخدام DELETE

عبارة DELETE تزيل الصفوف من جدول. مثل UPDATE، خطير بدون شرط WHERE!

الصيغة الأساسية

DELETE FROM table_name
WHERE condition;

يتطلب حذر شديد!

لا تنسَ أبداً شرط WHERE إلا إذا كنت تريد حذف جميع الصفوف!

-- كارثي: يحذف جميع المستخدمين!
DELETE FROM users;

-- آمن: يحذف مستخدم واحد محدد
DELETE FROM users WHERE id = 10;

مثال: حذف صف واحد

-- حذف منتج محدد
DELETE FROM products WHERE id = 5;

-- حذف مستخدم محدد
DELETE FROM users WHERE username = 'spam_account';

مثال: حذف صفوف متعددة

-- حذف جميع المنتجات غير النشطة
DELETE FROM products WHERE is_active = 0;

-- حذف الطلبات القديمة
DELETE FROM orders
WHERE created_at < DATE_SUB(NOW(), INTERVAL 2 YEAR);

-- حذف المستخدمين الذين لم يسجلوا الدخول أبداً
DELETE FROM users
WHERE last_login IS NULL
AND created_at < DATE_SUB(NOW(), INTERVAL 6 MONTH);

الحذف مع شروط متعددة

-- حذف الطلبات الملغاة الأقدم من 90 يوماً
DELETE FROM orders
WHERE status = 'cancelled'
AND created_at < DATE_SUB(NOW(), INTERVAL 90 DAY);

-- حذف التعليقات غير المرغوب فيها
DELETE FROM comments
WHERE (content LIKE '%viagra%' OR content LIKE '%casino%')
AND created_at > DATE_SUB(NOW(), INTERVAL 7 DAY);

الحذف مع LIMIT

-- حذف أقدم 100 إدخال سجل
DELETE FROM logs
ORDER BY created_at ASC
LIMIT 100;

اختبار الحذف بأمان

اختبر دائماً باستخدام SELECT أولاً

-- 1. معاينة ما سيتم حذفه
SELECT * FROM products WHERE is_active = 0;

-- 2. تحقق من العدد
SELECT COUNT(*) FROM products WHERE is_active = 0;

-- 3. إذا كان صحيحاً، تابع بالحذف
DELETE FROM products WHERE is_active = 0;

-- 4. تحقق من الحذف
SELECT COUNT(*) FROM products WHERE is_active = 0;  -- يجب أن يُرجع 0

الحذف الناعم مقابل الحذف الصلب

الحذف الصلب (دائم)

يزيل الصفوف فعلياً من قاعدة البيانات. البيانات ضائعة للأبد.

DELETE FROM users WHERE id = 10;

الحذف الناعم (موصى به للبيانات المهمة)

وضع علامة على الصفوف على أنها محذوفة بدلاً من إزالتها فعلياً. هذا يسمح بالاستعادة ويحافظ على سلامة الإشارات.

-- إضافة عمود deleted_at إلى جدولك
ALTER TABLE users ADD COLUMN deleted_at TIMESTAMP NULL;

-- "حذف" عن طريق تعيين الطابع الزمني
UPDATE users
SET deleted_at = NOW()
WHERE id = 10;

-- استبعاد السجلات "المحذوفة" في الاستعلامات
SELECT * FROM users WHERE deleted_at IS NULL;

-- استعادة سجل محذوف بشكل ناعم
UPDATE users
SET deleted_at = NULL
WHERE id = 10;

فوائد الحذف الناعم

  • قابل للاستعادة: يمكن استعادة البيانات المحذوفة عن طريق الخطأ
  • مسار التدقيق: الاحتفاظ بتاريخ من حذف ماذا ومتى
  • سلامة الإشارات: علاقات المفاتيح الخارجية تبقى سليمة
  • التحليلات: يمكن تحليل أنماط البيانات المحذوفة

استخدم الحذف الناعم لـ: حسابات المستخدمين، الطلبات، المنشورات، السجلات التجارية المهمة

استخدم الحذف الصلب لـ: السجلات، البيانات المؤقتة، الرسائل غير المرغوب فيها، بيانات الجلسة القديمة

TRUNCATE مقابل DELETE

DELETE

-- يحذف الصفوف واحدة تلو الأخرى
-- يمكن استخدام شرط WHERE
-- يتم تنفيذ المحفزات
-- أبطأ للجداول الكبيرة
-- يمكن التراجع (في المعاملات)
DELETE FROM logs WHERE created_at < '2023-01-01';

TRUNCATE

-- يزيل جميع الصفوف دفعة واحدة
-- لا يُسمح بشرط WHERE
-- أسرع بكثير
-- يعيد تعيين عداد AUTO_INCREMENT
-- لا يمكن التراجع
TRUNCATE TABLE logs;

تحذير TRUNCATE: لا يمكن التراجع عن TRUNCATE ويحذف جميع البيانات. استخدمه فقط عندما تكون متأكداً تماماً!

الحذف المتتالي

عندما تقوم بإعداد المفاتيح الخارجية باستخدام ON DELETE CASCADE، فإن حذف صف أصلي يحذف تلقائياً الصفوف الفرعية المرتبطة.

-- إعداد الجدول مع CASCADE
CREATE TABLE orders (
    id INT PRIMARY KEY AUTO_INCREMENT,
    user_id INT NOT NULL,
    FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
);

CREATE TABLE order_items (
    id INT PRIMARY KEY AUTO_INCREMENT,
    order_id INT NOT NULL,
    FOREIGN KEY (order_id) REFERENCES orders(id) ON DELETE CASCADE
);

-- حذف مستخدم يحذف تلقائياً طلباته وعناصر طلباته
DELETE FROM users WHERE id = 10;
-- هذا يحذف أيضاً:
-- - جميع طلبات المستخدم 10
-- - جميع order_items لتلك الطلبات

خيارات المفتاح الخارجي الأخرى

  • ON DELETE CASCADE: حذف الصفوف الفرعية تلقائياً
  • ON DELETE SET NULL: تعيين المفتاح الخارجي إلى NULL (يجب أن يسمح العمود بـ NULL)
  • ON DELETE RESTRICT: منع الحذف إذا كانت الصفوف الفرعية موجودة (افتراضي)
  • ON DELETE NO ACTION: نفس RESTRICT

الصفوف المتأثرة

بعد UPDATE أو DELETE، تحقق من عدد الصفوف المتأثرة:

-- في MySQL CLI
UPDATE products SET price = 99.99 WHERE id = 5;
-- يظهر: Query OK, 1 row affected

-- في PHP (mysqli)
$result = $mysqli->query("UPDATE products SET price = 99.99 WHERE id = 5");
$affected = $mysqli->affected_rows;
echo "Updated $affected rows";

-- في PHP (PDO)
$stmt = $pdo->prepare("UPDATE products SET price = ? WHERE id = ?");
$stmt->execute([99.99, 5]);
$affected = $stmt->rowCount();
echo "Updated $affected rows";

أمان المعاملات

للتحديثات/الحذف الحاسمة، استخدم المعاملات لضمان اتساق البيانات:

-- بدء المعاملة
START TRANSACTION;

-- تنفيذ التحديثات
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
UPDATE accounts SET balance = balance + 100 WHERE id = 2;

-- إذا كان كل شيء يبدو جيداً، قم بالالتزام
COMMIT;

-- إذا حدث خطأ، تراجع
ROLLBACK;

متى تستخدم المعاملات:

  • تحويل الأموال بين الحسابات
  • معالجة الطلبات (المخزون، الدفع، سجل الطلب)
  • حذف السجلات المرتبطة عبر جداول متعددة
  • أي عملية حيث الإنجاز الجزئي سيكون إشكالياً

أمثلة عملية

مثال 1: تحديث ملف تعريف المستخدم

-- تحديث معلومات ملف تعريف المستخدم
UPDATE users
SET
    full_name = 'John Smith',
    phone = '+1-555-0123',
    address = '123 Main St',
    updated_at = NOW()
WHERE id = 25;

مثال 2: إدارة حالة الطلب

-- وضع علامة على الطلب على أنه تم شحنه
UPDATE orders
SET
    status = 'shipped',
    shipped_at = NOW(),
    tracking_number = 'TRACK12345'
WHERE id = 100;

-- إلغاء الطلبات المعلقة الأقدم من 30 يوماً
UPDATE orders
SET status = 'cancelled'
WHERE status = 'pending'
AND created_at < DATE_SUB(NOW(), INTERVAL 30 DAY);

مثال 3: إدارة المخزون

-- تقليل المخزون بعد الشراء
UPDATE products
SET stock = stock - 2
WHERE id = 15 AND stock >= 2;

-- إعادة تخزين المنتجات
UPDATE products
SET stock = stock + 50, is_active = 1
WHERE id IN (10, 15, 20, 25);

مثال 4: تنظيف البيانات

-- حذف الحسابات غير المتحقق منها الأقدم من 7 أيام
DELETE FROM users
WHERE email_verified = 0
AND created_at < DATE_SUB(NOW(), INTERVAL 7 DAY);

-- حذف بيانات الجلسة القديمة
DELETE FROM sessions
WHERE last_activity < UNIX_TIMESTAMP(DATE_SUB(NOW(), INTERVAL 2 HOUR));

-- أرشفة وحذف السجلات القديمة
INSERT INTO archived_logs SELECT * FROM logs
WHERE created_at < DATE_SUB(NOW(), INTERVAL 1 YEAR);

DELETE FROM logs
WHERE created_at < DATE_SUB(NOW(), INTERVAL 1 YEAR);

تمرين: عمليات إدارة البيانات

اكتب عبارات SQL للسيناريوهات التالية:

  1. تحديث المنتج #5 ليكون له سعر 79.99 دولار ومخزون 50
  2. إعطاء خصم 15% لجميع المنتجات في فئة "الكتب"
  3. وضع علامة على جميع الطلبات بحالة "تم التسليم" من 2023 على أنها "مكتملة"
  4. حذف جميع التعليقات التي تحتوي على كلمة "spam"
  5. حذف ناعم (تعيين deleted_at) للمستخدم بالبريد الإلكتروني "test@example.com"
  6. تحديث عدد المشاهدات لمنشور المدونة #42 عن طريق زيادته بمقدار 1
  7. حذف جميع المنتجات التي كانت غير نشطة لأكثر من سنة واحدة
  8. تحديث جميع المستخدمين الذين سجلوا قبل 2020 ليكون لديهم علامة "legacy_user" معينة على true

تذكر: اكتب استعلامات SELECT أولاً لمعاينة الصفوف المتأثرة!

ملخص أفضل الممارسات

قائمة التحقق من سلامة UPDATE/DELETE

  • استخدم دائماً WHERE: إلا إذا كنت تريد عمداً التأثير على جميع الصفوف
  • اختبر باستخدام SELECT أولاً: معاينة الصفوف التي ستتأثر
  • احتفظ بنسخة احتياطية قبل العمليات المجمعة: أنشئ نسخة احتياطية لقاعدة البيانات للتحديثات/الحذف الكبيرة
  • استخدم LIMIT: اختبر على صفوف قليلة أولاً باستخدام LIMIT
  • تحقق من الصفوف المتأثرة: تحقق من العدد المتوقع من الصفوف المتغيرة
  • استخدم المعاملات: للعمليات متعددة الخطوات الحاسمة
  • فكر في الحذف الناعم: للبيانات المهمة التي قد تحتاج إلى استعادة
  • أضف الطوابع الزمنية: تتبع متى تم تحديث السجلات (عمود updated_at)
  • سجل التغييرات الحاسمة: احتفظ بمسار تدقيق للتعديلات المهمة
  • استخدم قيود المفاتيح الخارجية: حافظ على سلامة الإشارات

ما التالي؟

في الدرس التالي، ستتعلم تقنيات الاستعلام المتقدمة بما في ذلك GROUP BY و HAVING والاستعلامات الفرعية ودوال التجميع لإجراء تحليل بيانات معقد.