SQL: تدريب - استعلامات أساسية شاملة
في الدروس الخمسة الأولى، تعلمنا "الاستماع والتحدث والقراءة والكتابة" في SQL — إنشاء الجداول، الاستعلام، التصفية، الفرز، عمليات CRUD، وأنواع البيانات. حان الوقت لدمج هذه المهارات لحل مشكلات العمل الحقيقية. هذا الدرس لا يحتوي على بناء جملة جديد — فقط تدريب.
1. متطلبات المشروع
أنت محلل بيانات في شركة تجارة إلكترونية، وتحتاج لاستخراج المعلومات التالية من قاعدة بيانات الشركة:
- إدارة الموظفين: استعلام موظفي أقسام محددة، فرز حسب الراتب، ترقيم الصفحات
- تصفية المنتجات: تصفية المنتجات حسب نطاق السعر والمخزون، بحث غامض في أسماء المنتجات
- تحليل الطلبات: استعلام الطلبات الأخيرة، حساب مبالغ الطلبات، تحديث حالة الطلب
- صيانة البيانات: تحديث مجمع للأسعار، مسح البيانات منتهية الصلاحية، إدراج بيانات جديدة
تحتوي قاعدة البيانات على أربعة جداول: employees و departments و orders و products.
2. تنفيذ الشيفرة الكاملة
(1) الخطوة 1: إنشاء قاعدة البيانات والجداول
-- إنشاء جدول الأقسام
CREATE TABLE departments (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
location TEXT
);
-- إنشاء جدول الموظفين
CREATE TABLE employees (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
department_id INTEGER,
salary DECIMAL(10,2),
hire_date DATE,
FOREIGN KEY (department_id) REFERENCES departments(id)
);
-- إنشاء جدول المنتجات
CREATE TABLE products (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
category TEXT,
price DECIMAL(10,2),
stock INTEGER DEFAULT 0
);
-- إنشاء جدول الطلبات
CREATE TABLE orders (
id INTEGER PRIMARY KEY AUTOINCREMENT,
customer_name TEXT NOT NULL,
product_id INTEGER,
quantity INTEGER,
order_date DATE,
FOREIGN KEY (product_id) REFERENCES products(id)
);
(2) الخطوة 2: إدراج بيانات تجريبية
-- إدراج بيانات الأقسام
INSERT INTO departments (name, location) VALUES
('Engineering', 'New York'),
('Marketing', 'Los Angeles'),
('Finance', 'New York'),
('HR', 'Chicago');
-- إدراج بيانات الموظفين
INSERT INTO employees (name, department_id, salary, hire_date) VALUES
('John', 1, 15000.00, '2023-01-15'),
('Jane', 1, 18000.00, '2022-06-01'),
('Bob', 2, 12000.00, '2023-03-20'),
('Alice', 3, 13000.00, '2021-11-10'),
('Charlie', 1, 20000.00, '2020-08-05'),
('Diana', 2, 11000.00, '2024-01-10'),
('Eve', NULL, 9000.00, '2024-05-01'),
('Frank', 3, 14000.00, '2022-09-15');
-- إدراج بيانات المنتجات
INSERT INTO products (name, category, price, stock) VALUES
('iPhone 15', 'Phone', 5999.00, 100),
('MacBook Pro', 'Computer', 12999.00, 50),
('AirPods Pro', 'Accessory', 1899.00, 200),
('iPad Air', 'Tablet', 4799.00, 80),
('Apple Watch', 'Watch', 2999.00, 150),
('Magic Keyboard', 'Accessory', 999.00, 300),
('Mac Mini', 'Computer', 4499.00, 0),
('iPhone 14', 'Phone', 4999.00, 20);
-- إراج بيانات الطلبات
INSERT INTO orders (customer_name, product_id, quantity, order_date) VALUES
('Tom', 1, 1, '2024-06-01'),
('Lucy', 3, 2, '2024-06-02'),
('Mike', 2, 1, '2024-06-03'),
('Tom', 5, 1, '2024-06-05'),
('Sarah', 4, 3, '2024-06-10'),
('Lucy', 6, 5, '2024-06-15'),
('David', 1, 2, '2024-06-20'),
('Mike', 8, 1, '2024-06-25');
(3) الخطوة 3: استعلامات إدارة الموظفين
-- س1: استعلام جميع موظفي قسم الهندسة، مرتبة حسب الراتب من الأعلى إلى الأقل
SELECT name, salary, hire_date
FROM employees
WHERE department_id = 1
ORDER BY salary DESC;
الإخراج:
name salary hire_date
------- -------- ----------
Charlie 20000.00 2020-08-05
Jane 18000.00 2022-06-01
John 15000.00 2023-01-15
-- س2: استعلام أعلى 3 موظفين بالراتب (ترقيم صفحات: صفحة 1، 3 لكل صفحة)
SELECT name, salary, department_id
FROM employees
ORDER BY salary DESC
LIMIT 3 OFFSET 0;
الإخراج:
name salary department_id
------- -------- -------------
Charlie 20000.00 1
Jane 18000.00 1
John 15000.00 1
-- س3: استعلام الصفحة 2 (3 سجلات لكل صفحة)
SELECT name, salary, department_id
FROM employees
ORDER BY salary DESC
LIMIT 3 OFFSET 3;
الإخراج:
name salary department_id
----- -------- -------------
Frank 14000.00 3
Alice 13000.00 3
Bob 12000.00 2
-- س4: استعلام الموظفين without قسم معيّن
SELECT name, salary
FROM employees
WHERE department_id IS NULL;
الإخراج:
name salary
---- --------
Eve 9000.00
(4) الخطوة 4: استعلامات تصفية المنتجات
-- س5: استعلام المنتجات whose سعرها بين 1000 و 5000، مرتبة حسب السعر تصاعديًا
SELECT name, category, price, stock
FROM products
WHERE price BETWEEN 1000 AND 5000
ORDER BY price ASC;
الإخراج:
name category price stock
-------------- -------- ------- -----
AirPods Pro Accessory 1899.00 200
Apple Watch Watch 2999.00 150
Mac Mini Computer 4499.00 0
iPad Air Tablet 4799.00 80
-- س6: بحث غامض عن منتجات تحتوي على "iPhone" في الاسم
SELECT name, price, stock
FROM products
WHERE name LIKE '%iPhone%';
الإخراج:
name price stock
---------- ------- -----
iPhone 15 5999.00 100
iPhone 14 4999.00 20
-- س7: استعلام المنتجات المتوفرة (مخزون > 0) whose سعرها أقل من 3000
SELECT name, price, stock
FROM products
WHERE stock > 0 AND price < 3000
ORDER BY price DESC;
الإخراج:
name price stock
-------------- ------- -----
AirPods Pro 1899.00 200
Magic Keyboard 999.00 300
(5) الخطوة 5: استعلامات تحليل الطلبات
-- س8: استعلام آخر 5 طلبات، معرضًا اسم المنتج واسم العميل
SELECT o.customer_name, p.name AS product_name,
o.quantity, o.order_date
FROM orders o
JOIN products p ON o.product_id = p.id
ORDER BY o.order_date DESC
LIMIT 5;
الإخراج:
customer_name product_name quantity order_date
------------- ------------- -------- ----------
Mike iPhone 14 1 2024-06-25
David iPhone 15 2 2024-06-20
Lucy Magic Keyboard 5 2024-06-15
Sarah iPad Air 3 2024-06-10
Tom Apple Watch 1 2024-06-05
-- س9: استعلام مبلغ كل طلب (سعر الوحدة × الكمية)، مرتبة حسب المبلغ تنازليًا
SELECT o.customer_name, p.name AS product_name,
p.price, o.quantity,
(p.price * o.quantity) AS total_amount
FROM orders o
JOIN products p ON o.product_id = p.id
ORDER BY total_amount DESC;
الإخراج:
customer_name product_name price quantity total_amount
------------- ------------- -------- -------- ------------
Sarah iPad Air 4799.00 3 14397.00
Mike MacBook Pro 12999.00 1 12999.00
David iPhone 15 5999.00 2 11998.00
Tom iPhone 15 5999.00 1 5999.00
Lucy Magic Keyboard 999.00 5 4995.00
Mike iPhone 14 4999.00 1 4999.00
Tom Apple Watch 2999.00 1 2999.00
Lucy AirPods Pro 1899.00 2 3798.00
(6) الخطوة 6: عمليات صيانة البيانات
-- س10: زيادة سعر جميع منتجات فئة "Accessory" بنسبة 10%
-- أكد أولاً نطاق التأثير
SELECT name, price FROM products WHERE category = 'Accessory';
قبل التعديل:
name price
-------------- -------
AirPods Pro 1899.00
Magic Keyboard 999.00
-- نفّذ التحديث
UPDATE products
SET price = ROUND(price * 1.1, 2)
WHERE category = 'Accessory';
-- تحقق من النتائج
SELECT name, price FROM products WHERE category = 'Accessory';
بعد التعديل:
name price
-------------- -------
AirPods Pro 2088.90
Magic Keyboard 1098.90
-- س11: تحديد المنتجات نفدت المخزون (مخزون = 0) كمُوقَفة
-- هنا نحذف المنتجات نفدت المخزون كمثال
-- أكد أولاً
SELECT id, name, stock FROM products WHERE stock = 0;
نتيجة التأكيد:
id name stock
-- -------- -----
7 Mac Mini 0
-- حذف المنتجات نفدت المخزون
DELETE FROM products WHERE stock = 0;
-- س12: إدراج منتج جديد
INSERT INTO products (name, category, price, stock)
VALUES ('AirTag', 'Accessory', 229.00, 500);
-- عرض جميع المنتجات أخيرًا
SELECT id, name, category, price, stock
FROM products
ORDER BY id;
قائمة المنتجات النهائية:
id name category price stock
-- -------------- -------- -------- -----
1 iPhone 15 Phone 5999.00 100
2 MacBook Pro Computer 12999.00 50
3 AirPods Pro Accessory 2088.90 200
4 iPad Air Tablet 4799.00 80
5 Apple Watch Watch 2999.00 150
6 Magic Keyboard Accessory 1098.90 300
8 iPhone 14 Phone 4999.00 20
9 AirTag Accessory 229.00 500
3. مراجعة الشيفرة
| الاستعلام | المعرفة المستخدمة | التقنيات الأساسية |
|---|---|---|
| س1-س3 | SELECT + WHERE + ORDER BY + LIMIT/OFFSET | ترقيم الصفحات يستخدم LIMIT n OFFSET (page-1)*n |
| س4 | WHERE + IS NULL | يجب استخدام IS NULL للتحقق من NULL |
| س5 | BETWEEN + ORDER BY | BETWEEN يتضمن قيمتي النقطة النهاية |
| س6 | LIKE + حرف البدل % |
%كلمة_مفتاحية% يطابق أي موضع |
| س7 | شروط AND متعددة + عوامل مقارنة | دمج شروط للتصفية |
| س8 | JOIN + ORDER BY LIMIT | استعلامات متعددة الجداول |
| س9 | JOIN + حساب تعبير + ORDER BY | حسابات في SELECT |
| س10 | UPDATE + WHERE + ROUND | SELECT للتأكيد قبل التحديث |
| س11 | DELETE + WHERE | SELECT للتأكيد قبل الحذف |
| س12 | INSERT + التحقق النهائي | استعلام للتحقق بعد الإدراج |
▶ مثال
SELECT category, COUNT(*) AS total
FROM products
GROUP BY category
HAVING total > 5;
❓ أسئلة شائعة
س: مع ترقيم الصفحات LIMIT OFFSET، إذا أُضيفت أو حُذفت بيانات، هل ستكون هناك سجلات مكررة أو مفقودة؟ ج: نعم. إذا تغيرت البيانات أثناء ترقيم الصفحات، قد يؤدي ترقيم الصفحات القائم على OFFSET إلى سجلات مكررة أو مفقودة. النهج الأكثر استقرارًا هو "ترقيم المؤشر":
WHERE id > last_id_from_previous_page LIMIT 10، لكن هذا يتطلب أن يكون لكل سجل مفتاح فرز متسق.
س:
LIKE '%كلمة_مفتاحية%'يسبب فشل الفهرس. ماذا عن مجموعات البيانات الكبيرة؟ ج: LIKE الذي يبدأ بحرف بدل действительно لا يمكنه استخدام الفهرس العادي. لمجموعات البيانات الكبيرة، فكّر في: ① استخدام البحث النصي الكامل (FTS5، يدعمه SQLite)؛ ② استخدام محركات بحث مثل Elasticsearch على مستوى التطبيق؛ ③ إذا كانت المطابقة السابقة فقط مطلوبة، يمكن لـLIKE 'كلمة_مفتاحية%'استخدام الفهرس.
س: ماذا لو كان لجدولين نفس اسم العمود في استعلام JOIN؟ ج: استخدم أسماء مستعارة للجداول أو أسماء مستعارة للأعمدة للتمييز. على سبيل المثال:
SELECT e.name, d.name AS dept_name FROM employees e JOIN departments d ON e.department_id = d.id;. من الجيد عادة استخدام أسماء مستعارة للأعمدة لإخراج أوضح.
س: هل يمكن التراجع عن عمليات UPDATE و DELETE؟ ج: إذا كانت ضمن معاملة (بين BEGIN...COMMIT)، يمكنك استخدام ROLLBACK للتراجع. إذا تم COMMIT بالفعل، في SQLite يُستعاد بشكل أساسي ما لم تكن لديك نسخة احتياطية. MySQL يمكنه الاستعادة عبر إعادة تشغيل binlog. لذا قم دائمًا بالنسخ الاحتياطي قبل عمليات الإنتاج.
📖 ملخص
- ادمج SELECT + WHERE + ORDER BY + LIMIT لاستعلامات معقدة وترقيم الصفحات
- استخدم IS NULL للقيم الفارغة، LIKE للمطابقة الغامضة، BETWEEN لاستعلامات النطاق
- استعلامات متعددة الجداول تستخدم JOIN؛ الحقول المحسوبة يمكنها استخدام التعبيرات في SELECT
- SELECT للتأكيد قبل UPDATE/DELETE — هذه أهم عادة أمان
- ترقيم الصفحات يستخدم LIMIT + OFFSET؛ لمجموعات البيانات الكبيرة، فكّر في ترقيم المؤشر
- البحث الغامض الذي يبدأ بـ
%يُبطل الفهارس؛ لمجموعات البيانات الكبيرة، فكّر في البحث النصي الكامل
📝 تمارين
(1) التمرين 1 (⭐)
اكتب استعلامات لإكمال المهام التالية:
- استعلام جميع الموظفين الذين التحقوا عام 2023، مرتبة حسب تاريخ التوظيف تصاعديًا
- استعلام المنتج الأعلى سعرًا في فئة "Phone"
- استعلام جميع طلبات العميل "Tom"، معرضًا اسم المنتج والكمية
(2) التمرين 2 (⭐⭐)
اكتب استعلامات لإكمال المهام التالية:
- استعلام عدد الموظفين في كل قلم (تلميح: يتطلب GROUP BY — يمكنك الانتقال للدروس اللاحقة والعودة لتحدي هذا)
- استعلام إجمالي مبلغ جميع الطلبات
- خفض سعر منتجات فئة "Computer" بنسبة 5%، ثم استعلم للتحقق
(3) التمرين 3 (⭐⭐⭐)
محاكاة سيناريو "تنبيه المخزون المنخفض":
- استعلام جميع المنتجات whose مخزونها أقل من 50، مرتبة حسب المخزون تصاعديًا
- أدرج هذه المنتجات في جدول جديد
low_stock_alert - زد مخزون كل من هذه المنتجات بمقدار 100
- تحقق من نتائج التحديث