SQLتنفيذ كل الأوامر أو إلغاءها كلها
- مفهوم الـ Transaction
- تجهيز قاعدة البيانات التي سنطبق عليها
- طريقة وضع الكود بداخل Transaction
- التراجع عن تنفيذ أوامر الـ Transaction
مفهوم الـ Transaction
Transaction يقصد بها مجموعة من الأوامر إما أن تتنفذ جميعها بنجاح أو لا يتم تنفيذها.
هناك حالات كثيرة تكون فيها مضطر إلى تنفيذ مجموعة أوامر أو التراجع عن تنفيذها خاصةً إذا كنت تطور قاعدة بيانات لمشروع فيه معاملات مالية.
فمثلاً إذا كنت تنوي شراء برنامج من أي موقع إلكتروني، في هذه الحالة سيطلب منك الموقع إدخال معلومات بطاقتك المصرفية التي ستدفع من خلالها، بعد أن تدخل معلومات البطاقة، سيتم التأكد من أنك تملك المال المطلوب في البطاقة، إذا كنت تملك ثمن شراء البرنامج في بطاقتك، سيقوم الموقع بتسجيل أنه تم بيع نسخة من البرنامج لك، و ستجد أن الموقع أرسل لك كود تفعيل للبرنامج خاص بك، و ستجد أن بطاقتك المصرفية تم إقتطاع المبلغ الذي دفعته منها.
في حالة الشراء أونلاين قد تقع في مشاكل كارثية ما لم يتم تنفيذ جميع الخطوات بنجاح. فمثلاً في حال قمت بتحويل المال للموقع الإلكتروني و فجأةً تعطل الموقع قبل أن يسجل في قاعدة البيانات أنك قمت بشراء نسخة البرنامج و قبل أن يرسل لك كود تفعيل البرنامج الذي دفعت ثمنه فإنك ستخسر مالك.
Transaction تعني محاولة تنفيذ جميع الأوامر و في حال فشل أي أمر موضوع يجب إلغاء تنفيذ كل الأوامر التي تم تنفيذها بنجاح.
قواعد بيانات MySQL - SQL Server - Access - Oracle - SQLite جميعها تتيح لك إمكانية وضع الكود في Transaction و لكن أسلوب كتابتها تختلف قليلاً من قاعدة بيانات لأخرى.
تجهيز قاعدة البيانات التي سنطبق عليها
قم بتنفيذ الإستعلام التالي حتى تنشئ قاعدة بيانات جديدة إسمها harmash
و تنشئ فيها الجداول التالية:
users
جدول خاص لحفظ معلومات المستخدمين و قد وضعنا فيه بيانات 3 مستخدمين.products
جدول خاص لحفظ معلومات المنتجات و قد وضعنا فيه بيانات 5 منتجات.orders
جدول خاص لحفظ الطلبيات.
مثال
-- سيتم حذفها harmash في حال كان يوجد بالأساس قاعدة بيانات إسمها DROP DATABASE IF EXISTS harmash; -- harmash هنا قمنا بإنشاء قاعدة بيانات جديدة إسمها CREATE DATABASE harmash; -- harmash هنا قمنا بتحديد أن أي إستعلام جديد سيتم تنفيذه على قاعدة البيانات USE harmash; -- يتألف من 3 أعمدة users هنا قمنا بإنشاء جدول جديد إسمه CREATE TABLE users ( id INT NOT NULL PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50), email VARCHAR(100) ); -- يتألف من 3 أعمدة products هنا قمنا بإنشاء جدول جديد إسمه CREATE TABLE products ( id INT NOT NULL PRIMARY KEY AUTO_INCREMENT, title VARCHAR(200), available INT ); -- يتألف من عمودين orders هنا قمنا بإنشاء جدول جديد إسمه CREATE TABLE orders ( user_id INT, product_id INT, quantity INT, sale_date DATE ); -- أي أضفنا معلومات 3 مستخدمين ،users هنا قمنا بإضافة 3 أسطر في الجدول INSERT INTO users VALUES (null, 'Mhamad', 'mhamad@example.com'); INSERT INTO users VALUES (null, 'Hala', 'hala@example.com'); INSERT INTO users VALUES (null, 'Sarah', 'sarah@example.com'); -- أي أضفنا معلومات 5 منتجات products هنا قمنا بإضافة 5 أسطر في الجدول INSERT INTO products VALUES (null, 'Mouse', 86); INSERT INTO products VALUES (null, 'Keyboard', 140); INSERT INTO products VALUES (null, 'Printer', 54); INSERT INTO products VALUES (null, 'Screen', 32); INSERT INTO products VALUES (null, 'Chair', 80);
فيما يلي البيانات التي قمنا بإضافتها في الجدول users
.
users | ||
---|---|---|
id | username | |
1 | Mhamad | mhamad@example.com |
2 | Hala | mhamad@example.com |
3 | Sarah | mhamad@example.com |
فيما يلي البيانات التي قمنا بإضافتها في الجدول products
.
products | ||
---|---|---|
id | title | available |
1 | Mouse | 86 |
2 | Keyboard | 140 |
3 | Printer | 45 |
4 | Screen | 32 |
5 | Chair | 80 |
فيما يلي البيانات التي قمنا بإضافتها في الجدول orders
.
orders | |||
---|---|---|---|
user_id | product_id | quantity | sale_date |
طريقة وضع الكود بداخل Transaction
في قواعد بيانات MySQL يجب أولاً إعلام قاعدة البيانات بأنك تريد إيقاف الحفظ التلقائي و هذه الخطوة تحتاج أن تفعلها مرة واحدة فقط و ليس عند إرسال كل إستعلام.
لإعلام قاعدة البيانات بأنك تريد إيقاف الحفظ التلقائي، يجب أن تنفذ الإستعلام التالي.
SET autocommit = OFF;
أسلوب قواعد بيانات MySQL / Oracle / SQLite
BEGIN; -- Statements COMMIT;
أسلوب قواعد بيانات SQL Server / ACCESS
BEGIN TRANSACTION; -- Statements COMMIT;
خطوات بناء Transaction
- نضع الأمر
BEGIN
أوBEGIN TRANSACTION
على حسب نوع قاعدة البيانات التي نستخدمها. - نضع الأوامر التي نريدها إما أن تتنفذ جميعها بنجاح و إما لا يتم تنفيذها.
- نضع الأمر
COMMIT
لحفظ جميع التغييرات إذا تم تنفيذ جميع الأوامر بنجاح.
في الإستعلام التالي إفترضنا أننا نقوم بتخزين عملية شراء لمنتج.
- المنتج الذي تم بيعه إفترضنا أنه يملك
id
يساوي4
. - الشخص الذي قام بشراء المنتج إفترضنا أنه يملك
id
يساوي1
.
إذا تمت عملية البيع بنجاح، يجب إنقاص كمية المنتج الذي تم بيعه في جدول المنتجات products
.
أيضاً، يجب إضافة id
الشخص الذي قام بالشراء و id
المنتج الذي تم بيعه في جدول الطلبيات orders
.
بما أنه يجب تنفيذ العمليتين سويةً أو عدم تنفيذهما من الأساس في حال حدث خطأ ما، سنقوم بوضع العملية بداخل Transaction.
مثال
-- Transaction هنا قمنا ببدء BEGIN; -- 4 يساوي id يساوي 1 إشترى 10 قطع من المنتج الذي يملك id هنا إفترضنا أن المستخدم الذي يملك INSERT INTO orders (user_id, product_id, quantity, sale_date) values (1, 4, 10, '2024-07-11'); -- يساوي 1 بهدف إنقاص 10 منها id للمنتج الذي يملك available هنا إفترضنا أننا نريد تحديث قيمة الحقل UPDATE products SET available = 76 WHERE id = 1; -- سيتم حفظ التغيرات في قاعدة البيانات Transaction إذا لم يحدث أي خطأ في الأوامر الموضوعة في الـ COMMIT;
النتيجة
1 row(s) inserted
1 row(s) affected
0 row(s) affected
النتيجة تعني أنه تم تنفيذ الأوامر BEGIN
و INSERT
و UPDATE
و COMMIT
بنجاح.
إذا قمت بعرض بيانات الجدول products
ستلاحظ أنه تم تحديث قيمة الحقل quantity
حيث كانت 86 ثم أصبحت 76.
products | ||
---|---|---|
id | title | available |
1 | Mouse | 76 |
2 | Keyboard | 140 |
3 | Printer | 45 |
4 | Screen | 32 |
5 | Chair | 80 |
و إذا قمت بعرض بيانات الجدول orders
ستلاحظ أنه تم إضافة سطر جديد فيه كما يلي.
orders | |||
---|---|---|---|
user_id | product_id | quantity | sale_date |
1 | 4 | 10 | 2024-07-11 |
التراجع عن تنفيذ أوامر الـ Transaction
لحفظ كل التغيرات التي تم إجراءها بداخل Transaction قلنا أنه يجب تنفيذ الأمر COMMIT
.
الآن للتراجع عن كل التغيرات التي تم إجراءها في قاعدة البيانات (أي لعدم حفظها)، يمكن تنفيذ الأمر ROLLBACK
ما لم يتم تنفيذ الأمر COMMIT
بعد لأنه لا يمكننا التراجع عن تنفيذ الأوامر في حال سبق و تم حفظها.
في الإستعلام التالي إفترضنا أننا نقوم بتخزين عملية شراء لمنتج و لكننا في النهاية قررنا إلغاء العملية.
- المنتج الذي تم بيعه إفترضنا أنه يملك
id
يساوي5
. - الشخص الذي قام بشراء المنتج إفترضنا أنه يملك
id
يساوي2
.
إذا تمت عملية البيع بنجاح، يجب إنقاص كمية المنتج الذي تم بيعه في جدول المنتجات products
.
أيضاً، يجب إضافة id
الشخص الذي قام بالشراء و id
المنتج الذي تم بيعه في جدول الطلبيات orders
.
بما أنه يجب تنفيذ العمليتين سويةً أو عدم تنفيذهما من الأساس في حال حدث خطأ ما، سنقوم بوضع العملية بداخل Transaction.
في النهاية، بما أننا سنقوم بإلغاء تنفيذ العمليتين فإننا سنستدعي الأمر ROLLBACK
.
مثال
-- Transaction هنا قمنا ببدء BEGIN; -- 5 يساوي id يساوي 2 بهدف إشترى 30 قطعة من المنتج الذي يملك id هنا إفترضنا أن المستخدم الذي يملك INSERT INTO orders (user_id, product_id, quantity, sale_date) values (5, 2, 30, '2024-07-11'); -- يساوي 5 بهدف إنقاص 30 منها id للمنتج الذي يملك available هنا إفترضنا أننا نريد تحديث قيمة الحقل UPDATE products SET available = 50 WHERE id = 5; -- INSERT و UPDATE للتراجع عن تنفيذ الأمرين ROLLBACK هنا قمنا باستدعاء الأمر ROLLBACK;
النتيجة
1 row(s) inserted
1 row(s) affected
0 row(s) affected
النتيجة تعني أنه تم تنفيذ الأوامر BEGIN
و INSERT
و UPDATE
و ROLLBACK
بنجاح.
إذا قمت بعرض بيانات الجدول products
ستلاحظ أنها لم تتغير.
products | ||
---|---|---|
id | title | available |
1 | Mouse | 76 |
2 | Keyboard | 140 |
3 | Printer | 45 |
4 | Screen | 32 |
5 | Chair | 80 |
و إذا قمت بعرض بيانات الجدول orders
ستلاحظ أنه لم تتغير أيضاُ.
orders | |||
---|---|---|---|
user_id | product_id | quantity | sale_date |
1 | 4 | 10 | 2024-07-11 |