المعاملات (Transactions)

المعاملات (Transactions) في SQL هي مجموعة من العمليات التي تُنفذ كوحدة واحدة غير قابلة للتجزئة. إما أن تُنفذ جميع العمليات بنجاح، أو لا تُنفذ أي منها على الإطلاق. المعاملات هي حجر الأساس لضمان سلامة البيانات (Data Integrity) في قواعد البيانات، خاصة في التطبيقات الحرجة مثل الأنظمة المالية والمصرفية. تخيل عملية تحويل مالي بين حسابين: يجب خصم المبلغ من الحساب الأول وإضافته للحساب الثاني. إذا نجحت عملية الخصم لكن فشلت عملية الإضافة، ستفقد الأموال. المعاملات تضمن أن العمليتين تحدثان معاً أو لا تحدثان على الإطلاق. في هذا الدرس الشامل والمفصل من سلسلة تعلم لغة SQL باللغة العربية، سنتعلم كل شيء عن المعاملات: ما هي، خصائص ACID، كيفية استخدامها، مستويات العزل، معالجة الأخطاء، وأمثلة عملية تطبيقية مفصلة.

1. ما هي المعاملة (Transaction)؟

المعاملة (Transaction) هي تسلسل من عملية واحدة أو أكثر من عمليات قاعدة البيانات تُعامل كوحدة منطقية واحدة. المعاملة تتبع مبدأ "الكل أو لا شيء" (All or Nothing): إما أن تُنفذ جميع العمليات بنجاح وتُحفظ التغييرات نهائياً (COMMIT)، أو إذا فشلت أي عملية، تُلغى جميع التغييرات وتعود قاعدة البيانات لحالتها السابقة (ROLLBACK).

في الحياة اليومية، نتعامل مع معاملات باستمرار. عندما تسحب نقوداً من ماكينة الصراف الآلي، تحدث عدة عمليات: التحقق من الرصيد، خصم المبلغ من حسابك، تسجيل العملية، وإخراج النقود. إذا فشلت أي خطوة (مثل نفاد النقود في الماكينة)، يجب إلغاء جميع الخطوات السابقة. لا يمكن خصم المبلغ من حسابك دون إعطائك النقود.

في قواعد البيانات، المعاملات ضرورية للحفاظ على تناسق البيانات (Data Consistency) في البيئات متعددة المستخدمين. عندما يعمل عدة مستخدمين على نفس البيانات في نفس الوقت، المعاملات تضمن أن كل مستخدم يرى حالة متسقة للبيانات، وأن العمليات المتزامنة لا تتداخل بطريقة تفسد البيانات.

2. خصائص ACID للمعاملات

المعاملات في قواعد البيانات يجب أن تحقق أربع خصائص أساسية تُعرف باختصار ACID:

A - Atomicity (الذرية)

المعاملة هي وحدة ذرية غير قابلة للتجزئة. إما أن تُنفذ جميع العمليات، أو لا تُنفذ أي منها. لا يوجد حالة وسطى. إذا فشلت أي عملية في المعاملة، تُلغى جميع العمليات السابقة تلقائياً.

مثال: في عملية تحويل مالي، إذا نجح خصم المبلغ من الحساب الأول لكن فشلت إضافته للحساب الثاني، يتم إلغاء عملية الخصم تلقائياً.

C - Consistency (التناسق)

المعاملة تنقل قاعدة البيانات من حالة متسقة إلى حالة متسقة أخرى. جميع قواعد سلامة البيانات (Constraints, Triggers, etc.) يجب أن تكون محققة قبل وبعد المعاملة.

مثال: إذا كان هناك قيد يمنع الرصيد السالب، المعاملة لن تكتمل إذا كانت ستؤدي لرصيد سالب.

I - Isolation (العزل)

المعاملات المتزامنة تُنفذ بشكل معزول عن بعضها البعض. التغييرات التي تجريها معاملة لا تكون مرئية للمعاملات الأخرى حتى تكتمل (COMMIT). هذا يمنع التداخل والتضارب بين المعاملات.

مثال: إذا كان مستخدمان يحاولان تحديث نفس الصف في نفس الوقت، العزل يضمن أن تغييرات أحدهما لا تؤثر على الآخر حتى تكتمل المعاملة.

D - Durability (الديمومة)

بمجرد أن تكتمل المعاملة بنجاح (COMMIT)، التغييرات تصبح دائمة ومحفوظة حتى لو حدث عطل في النظام أو انقطاع في الكهرباء. قاعدة البيانات تضمن أن التغييرات المُلتزم بها لن تُفقد.

مثال: بعد تأكيد عملية شراء، حتى لو انقطعت الكهرباء فوراً، الطلب سيبقى محفوظاً في قاعدة البيانات.

3. أوامر المعاملات الأساسية

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

-- أو
BEGIN;
تأكيد المعاملة: COMMIT
حفظ التغييرات نهائياً
-- حفظ جميع التغييرات في المعاملة
COMMIT;
إلغاء المعاملة: ROLLBACK
إلغاء جميع التغييرات
-- إلغاء جميع التغييرات والعودة لحالة ما قبل المعاملة
ROLLBACK;
مثال بسيط كامل
معاملة بسيطة كاملة
-- بدء المعاملة
START TRANSACTION;

-- إدراج موظف جديد
INSERT INTO employees (first_name, last_name, salary)
VALUES ('أحمد', 'محمد', 5000);

-- تحديث ميزانية القسم
UPDATE departments 
SET budget = budget + 5000 
WHERE department_name = 'IT';

-- تأكيد المعاملة (حفظ التغييرات)
COMMIT;

-- الآن التغييرات محفوظة نهائياً
مثال مع ROLLBACK
إلغاء معاملة
-- بدء المعاملة
START TRANSACTION;

-- حذف جميع الموظفين (خطأ!)
DELETE FROM employees;

-- التحقق من عدد الصفوف المحذوفة
SELECT COUNT(*) FROM employees; -- النتيجة: 0

-- إلغاء المعاملة (استرجاع البيانات)
ROLLBACK;

-- التحقق مرة أخرى
SELECT COUNT(*) FROM employees; -- الموظفون عادوا!

4. مثال عملي: تحويل مالي بين حسابين

هذا هو المثال الكلاسيكي لاستخدام المعاملات: تحويل مبلغ من حساب إلى آخر.

إعداد جدول الحسابات
-- جدول الحسابات البنكية
CREATE TABLE accounts (
    account_id INT PRIMARY KEY AUTO_INCREMENT,
    account_holder VARCHAR(100) NOT NULL,
    balance DECIMAL(15, 2) NOT NULL DEFAULT 0,
    CHECK (balance >= 0)
);

-- إدراج حسابين للاختبار
INSERT INTO accounts (account_holder, balance)
VALUES 
    ('أحمد محمد', 10000.00),
    ('فاطمة علي', 5000.00);
تحويل ناجح
عملية تحويل ناجحة
-- بدء المعاملة
START TRANSACTION;

-- خصم 1000 من حساب أحمد (ID = 1)
UPDATE accounts 
SET balance = balance - 1000 
WHERE account_id = 1;

-- إضافة 1000 لحساب فاطمة (ID = 2)
UPDATE accounts 
SET balance = balance + 1000 
WHERE account_id = 2;

-- التحقق من الأرصدة
SELECT * FROM accounts;

-- تأكيد المعاملة
COMMIT;

-- الآن التحويل مكتمل ومحفوظ نهائياً
تحويل فاشل مع ROLLBACK
عملية تحويل فاشلة
-- بدء المعاملة
START TRANSACTION;

-- محاولة خصم 15000 من حساب أحمد (رصيده 9000 فقط)
UPDATE accounts 
SET balance = balance - 15000 
WHERE account_id = 1;
-- هذا سيفشل بسبب قيد CHECK (balance >= 0)

-- بما أن العملية فشلت، نلغي المعاملة
ROLLBACK;

-- الرصيد لم يتغير

5. نقاط الحفظ: SAVEPOINT

نقاط الحفظ (Savepoints) تسمح لك بإنشاء نقاط وسيطة داخل المعاملة يمكنك الرجوع إليها دون إلغاء المعاملة بالكامل.

استخدام SAVEPOINT
-- بدء المعاملة
START TRANSACTION;

-- عملية 1: إدراج موظف
INSERT INTO employees (first_name, last_name, salary)
VALUES ('سارة', 'أحمد', 4000);

-- إنشاء نقطة حفظ
SAVEPOINT after_insert;

-- عملية 2: تحديث الراتب
UPDATE employees SET salary = salary * 1.1 WHERE last_name = 'أحمد';

-- إنشاء نقطة حفظ أخرى
SAVEPOINT after_update;

-- عملية 3: حذف (خطأ!)
DELETE FROM employees WHERE department = 'IT';

-- العودة لنقطة الحفظ الثانية (إلغاء الحذف فقط)
ROLLBACK TO SAVEPOINT after_update;

-- تأكيد المعاملة (الإدراج والتحديث محفوظان، الحذف ملغى)
COMMIT;
مثال عملي: معالجة طلب مع نقاط حفظ
معالجة طلب مع نقاط حفظ متعددة
START TRANSACTION;

-- 1. إنشاء الطلب
INSERT INTO orders (customer_id, order_date, total_amount)
VALUES (1, NOW(), 0);

SET @order_id = LAST_INSERT_ID();
SAVEPOINT order_created;

-- 2. إضافة منتجات للطلب
INSERT INTO order_items (order_id, product_id, quantity, price)
VALUES (@order_id, 101, 2, 50.00);

SAVEPOINT items_added;

-- 3. تحديث المخزون
UPDATE products SET stock = stock - 2 WHERE product_id = 101;

SAVEPOINT stock_updated;

-- 4. حساب الإجمالي
UPDATE orders SET total_amount = (
    SELECT SUM(quantity * price) FROM order_items WHERE order_id = @order_id
) WHERE order_id = @order_id;

-- إذا حدث خطأ في أي مرحلة، يمكن الرجوع لنقطة حفظ معينة
-- مثلاً: ROLLBACK TO SAVEPOINT items_added;

-- تأكيد المعاملة
COMMIT;

6. مستويات العزل (Isolation Levels)

مستوى العزل يحدد كيف تتعامل المعاملات المتزامنة مع بعضها. هناك أربعة مستويات قياسية:

مستوى العزل Dirty Read Non-Repeatable Read Phantom Read
READ UNCOMMITTED ممكن ممكن ممكن
READ COMMITTED مستحيل ممكن ممكن
REPEATABLE READ مستحيل مستحيل ممكن
SERIALIZABLE مستحيل مستحيل مستحيل
شرح المصطلحات
  • Dirty Read: قراءة بيانات لم تُلتزم بها بعد من معاملة أخرى
  • Non-Repeatable Read: قراءة نفس الصف مرتين في معاملة واحدة وإيجاد قيم مختلفة
  • Phantom Read: قراءة مجموعة صفوف مرتين وإيجاد صفوف جديدة أو مفقودة
تعيين مستوى العزل
تعيين مستوى العزل
-- تعيين مستوى العزل للمعاملة التالية
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
START TRANSACTION;
-- عمليات المعاملة
COMMIT;

-- أو تعيين مستوى العزل للجلسة بالكامل
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;

-- عرض مستوى العزل الحالي
SELECT @@transaction_isolation;

7. معالجة الأخطاء في المعاملات

من المهم جداً معالجة الأخطاء بشكل صحيح في المعاملات لضمان سلامة البيانات.

معالجة الأخطاء في إجراء مخزن
DELIMITER //

CREATE PROCEDURE transfer_money(
    IN from_account INT,
    IN to_account INT,
    IN amount DECIMAL(15, 2),
    OUT success BOOLEAN,
    OUT message VARCHAR(255)
)
BEGIN
    DECLARE from_balance DECIMAL(15, 2);
    DECLARE EXIT HANDLER FOR SQLEXCEPTION
    BEGIN
        ROLLBACK;
        SET success = FALSE;
        SET message = 'فشلت عملية التحويل';
    END;
    
    -- بدء المعاملة
    START TRANSACTION;
    
    -- التحقق من الرصيد
    SELECT balance INTO from_balance
    FROM accounts
    WHERE account_id = from_account;
    
    IF from_balance < amount THEN
        ROLLBACK;
        SET success = FALSE;
        SET message = 'الرصيد غير كافٍ';
    ELSE
        -- خصم المبلغ
        UPDATE accounts 
        SET balance = balance - amount 
        WHERE account_id = from_account;
        
        -- إضافة المبلغ
        UPDATE accounts 
        SET balance = balance + amount 
        WHERE account_id = to_account;
        
        -- تأكيد المعاملة
        COMMIT;
        SET success = TRUE;
        SET message = 'تمت عملية التحويل بنجاح';
    END IF;
END //

DELIMITER ;

-- استخدام الإجراء
CALL transfer_money(1, 2, 500, @success, @message);
SELECT @success, @message;

8. مثال عملي شامل: نظام حجز تذاكر

مثال واقعي يوضح استخدام المعاملات في نظام حجز تذاكر الطيران.

الجداول الأساسية
CREATE TABLE flights (
    flight_id INT PRIMARY KEY,
    flight_number VARCHAR(10),
    available_seats INT,
    price DECIMAL(10, 2)
);

CREATE TABLE bookings (
    booking_id INT PRIMARY KEY AUTO_INCREMENT,
    flight_id INT,
    passenger_name VARCHAR(100),
    seat_number VARCHAR(5),
    booking_date DATETIME DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (flight_id) REFERENCES flights(flight_id)
);

CREATE TABLE payments (
    payment_id INT PRIMARY KEY AUTO_INCREMENT,
    booking_id INT,
    amount DECIMAL(10, 2),
    payment_status VARCHAR(20),
    FOREIGN KEY (booking_id) REFERENCES bookings(booking_id)
);
إجراء الحجز الكامل
إجراء حجز تذكرة مع معاملة
DELIMITER //

CREATE PROCEDURE book_flight(
    IN p_flight_id INT,
    IN p_passenger_name VARCHAR(100),
    IN p_seat_number VARCHAR(5),
    OUT p_booking_id INT,
    OUT p_success BOOLEAN,
    OUT p_message VARCHAR(255)
)
BEGIN
    DECLARE v_available_seats INT;
    DECLARE v_price DECIMAL(10, 2);
    
    DECLARE EXIT HANDLER FOR SQLEXCEPTION
    BEGIN
        ROLLBACK;
        SET p_success = FALSE;
        SET p_message = 'فشل الحجز بسبب خطأ في النظام';
        SET p_booking_id = 0;
    END;
    
    START TRANSACTION;
    
    -- التحقق من توفر المقاعد
    SELECT available_seats, price
    INTO v_available_seats, v_price
    FROM flights
    WHERE flight_id = p_flight_id
    FOR UPDATE; -- قفل الصف لمنع الحجوزات المتزامنة
    
    IF v_available_seats <= 0 THEN
        ROLLBACK;
        SET p_success = FALSE;
        SET p_message = 'لا توجد مقاعد متاحة';
        SET p_booking_id = 0;
    ELSE
        -- إنشاء الحجز
        INSERT INTO bookings (flight_id, passenger_name, seat_number)
        VALUES (p_flight_id, p_passenger_name, p_seat_number);
        
        SET p_booking_id = LAST_INSERT_ID();
        
        -- تسجيل الدفع
        INSERT INTO payments (booking_id, amount, payment_status)
        VALUES (p_booking_id, v_price, 'pending');
        
        -- تحديث المقاعد المتاحة
        UPDATE flights
        SET available_seats = available_seats - 1
        WHERE flight_id = p_flight_id;
        
        COMMIT;
        SET p_success = TRUE;
        SET p_message = 'تم الحجز بنجاح';
    END IF;
END //

DELIMITER ;

-- اختبار الإجراء
INSERT INTO flights VALUES (1, 'SA123', 50, 500.00);

CALL book_flight(1, 'أحمد محمد', '12A', @booking_id, @success, @msg);
SELECT @booking_id, @success, @msg;

9. أفضل الممارسات للمعاملات

ما يجب فعله
  • اجعل المعاملات قصيرة قدر الإمكان
  • استخدم المعاملات فقط عند الضرورة
  • تأكد دائماً من إنهاء المعاملة بـ COMMIT أو ROLLBACK
  • استخدم معالجة الأخطاء المناسبة
  • اختر مستوى العزل المناسب لاحتياجاتك
  • استخدم FOR UPDATE عند الحاجة لقفل الصفوف
  • اختبر المعاملات في بيئة التطوير أولاً
ما يجب تجنبه
  • لا تترك معاملات مفتوحة لفترة طويلة
  • لا تضع تفاعل المستخدم داخل المعاملة
  • لا تستخدم مستوى عزل أعلى من اللازم
  • لا تنسَ COMMIT أو ROLLBACK
  • لا تضع عمليات I/O بطيئة داخل المعاملة
  • تجنب المعاملات المتداخلة
تحذير مهم:

المعاملات الطويلة أو المعلقة يمكن أن تسبب قفل (Lock) للجداول وتؤثر سلباً على أداء قاعدة البيانات. تأكد دائماً من إنهاء المعاملات في أسرع وقت ممكن.

10. المعاملات الضمنية مقابل الصريحة

المعاملات الضمنية (Implicit)

في MySQL، كل أمر SQL منفرد يُنفذ في معاملة ضمنية تلقائية (Auto-commit mode). إذا نجح الأمر، يُلتزم به تلقائياً. إذا فشل، يُلغى تلقائياً.

معاملة ضمنية
-- هذا الأمر يُنفذ في معاملة ضمنية
INSERT INTO employees (first_name, last_name) VALUES ('أحمد', 'محمد');
-- يُلتزم به تلقائياً بعد التنفيذ
المعاملات الصريحة (Explicit)

عندما تستخدم START TRANSACTION، تبدأ معاملة صريحة وتتحكم أنت في متى تُلتزم أو تُلغى.

التحكم في Auto-commit
-- تعطيل Auto-commit
SET autocommit = 0;

-- الآن جميع الأوامر تحتاج COMMIT يدوي
INSERT INTO employees (first_name, last_name) VALUES ('سارة', 'أحمد');
UPDATE employees SET salary = 5000 WHERE employee_id = 1;

-- يجب تأكيد التغييرات يدوياً
COMMIT;

-- إعادة تفعيل Auto-commit
SET autocommit = 1;
ملخص الدرس

في هذا الدرس الشامل، تعلمنا كل شيء عن المعاملات (Transactions) في SQL:

  • ما هي المعاملات ومبدأ "الكل أو لا شيء"
  • خصائص ACID: Atomicity, Consistency, Isolation, Durability
  • الأوامر الأساسية: START TRANSACTION, COMMIT, ROLLBACK
  • نقاط الحفظ: SAVEPOINT
  • مستويات العزل وتأثيرها
  • معالجة الأخطاء في المعاملات
  • أمثلة عملية: تحويل مالي، حجز تذاكر
  • أفضل الممارسات والتحذيرات

المعاملات هي أساس ضمان سلامة البيانات في قواعد البيانات. فهمها واستخدامها بشكل صحيح ضروري لبناء تطبيقات موثوقة وآمنة.

الخطوة التالية: الأمان في SQL (SQL Security)

أكمل رحلتك التعليمية وانتقل إلى الدرس التالي لتعلم الأمان في ( Security) وتطوير مهاراتك في قواعد البيانات.

الانتقال إلى الدرس التالي
المحرر الذكي

اكتب الكود وشاهد النتيجة فوراً

جرب الآن مجاناً
قناة ديف عربي

تابع أحدث الدروس والتحديثات مباشرة على واتساب

انضم الآن