مقدمة عن SQL
ما هي SQL؟
SQL (Structured Query Language) هي لغة للتعامل مع قواعد البيانات العلائقية. تم تطويرها في السبعينيات من قبل IBM.
مميزات SQL:
- لغة قياسية لمعظم قواعد البيانات
- سهلة التعلم والفهم
- قوية في التعامل مع البيانات
- تدعم عمليات معقدة
أنواع أوامر SQL:
- DDL: Data Definition Language (CREATE, ALTER, DROP)
- DML: Data Manipulation Language (SELECT, INSERT, UPDATE, DELETE)
- DCL: Data Control Language (GRANT, REVOKE)
قواعد البيانات والجداول
إنشاء قاعدة بيانات:
CREATE DATABASE mydb;
USE mydb;
حذف قاعدة بيانات:
DROP DATABASE mydb;
عرض قواعد البيانات:
SHOW DATABASES;
أنواع البيانات
أنواع البيانات الشائعة:
| النوع | الوصف | مثال |
|---|---|---|
INT |
عدد صحيح | 25 |
VARCHAR(n) |
نص متغير الطول | 'أحمد' |
CHAR(n) |
نص ثابت الطول | 'ABC' |
DATE |
تاريخ | '2024-01-15' |
DECIMAL(p,s) |
عدد عشري | 99.99 |
BOOLEAN |
قيمة منطقية | TRUE/FALSE |
CREATE TABLE
إنشاء جدول:
CREATE TABLE users (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(100) NOT NULL,
email VARCHAR(100) UNIQUE,
age INT,
created_at DATE DEFAULT CURRENT_DATE
);
تعديل جدول:
ALTER TABLE users
ADD COLUMN phone VARCHAR(20);
ALTER TABLE users
DROP COLUMN phone;
ALTER TABLE users
MODIFY COLUMN name VARCHAR(200);
حذف جدول:
DROP TABLE users;
SELECT - الأساسيات
SELECT بسيط:
-- اختيار جميع الأعمدة
SELECT * FROM users;
-- اختيار أعمدة محددة
SELECT name, email FROM users;
-- اختيار مع أسماء مستعارة
SELECT name AS الاسم, email AS البريد FROM users;
-- اختيار قيم مميزة
SELECT DISTINCT city FROM users;
WHERE Clause
شروط WHERE:
-- مقارنة
SELECT * FROM users WHERE age > 25;
SELECT * FROM users WHERE age >= 18;
SELECT * FROM users WHERE age < 30;
SELECT * FROM users WHERE age <= 25;
SELECT * FROM users WHERE age = 25;
SELECT * FROM users WHERE age != 25;
-- AND و OR
SELECT * FROM users WHERE age > 25 AND city = 'القاهرة';
SELECT * FROM users WHERE age > 25 OR city = 'الإسكندرية';
-- IN و NOT IN
SELECT * FROM users WHERE city IN ('القاهرة', 'الإسكندرية');
SELECT * FROM users WHERE city NOT IN ('القاهرة');
-- LIKE (البحث)
SELECT * FROM users WHERE name LIKE 'أحمد%';
SELECT * FROM users WHERE email LIKE '%@gmail.com';
-- BETWEEN
SELECT * FROM users WHERE age BETWEEN 20 AND 30;
-- IS NULL و IS NOT NULL
SELECT * FROM users WHERE email IS NULL;
SELECT * FROM users WHERE email IS NOT NULL;
ORDER BY
ترتيب النتائج:
-- ترتيب تصاعدي
SELECT * FROM users ORDER BY name ASC;
-- ترتيب تنازلي
SELECT * FROM users ORDER BY age DESC;
-- ترتيب متعدد
SELECT * FROM users ORDER BY city ASC, age DESC;
-- ترتيب حسب رقم العمود
SELECT name, email, age FROM users ORDER BY 3 DESC;
INSERT
إضافة بيانات:
-- إضافة سجل واحد
INSERT INTO users (name, email, age)
VALUES ('أحمد', 'ahmed@example.com', 25);
-- إضافة عدة سجلات
INSERT INTO users (name, email, age)
VALUES
('محمد', 'mohamed@example.com', 30),
('علي', 'ali@example.com', 28);
-- إضافة بدون تحديد الأعمدة (يجب ترتيب القيم)
INSERT INTO users VALUES (1, 'أحمد', 'ahmed@example.com', 25);
-- إضافة من جدول آخر
INSERT INTO users_backup
SELECT * FROM users WHERE age > 25;
UPDATE
تحديث البيانات:
-- تحديث سجل واحد
UPDATE users
SET email = 'newemail@example.com'
WHERE id = 1;
-- تحديث عدة أعمدة
UPDATE users
SET name = 'أحمد محمد', age = 26
WHERE id = 1;
-- تحديث عدة سجلات
UPDATE users
SET city = 'القاهرة'
WHERE city IS NULL;
-- تحديث بشرط معقد
UPDATE users
SET age = age + 1
WHERE age < 18;
DELETE
حذف البيانات:
-- حذف سجل واحد
DELETE FROM users WHERE id = 1;
-- حذف عدة سجلات
DELETE FROM users WHERE age < 18;
-- حذف جميع السجلات (احذر!)
DELETE FROM users;
-- حذف مع JOIN
DELETE u FROM users u
INNER JOIN orders o ON u.id = o.user_id
WHERE o.total < 100;
JOIN - المقدمة
ما هو JOIN؟
JOIN يستخدم لربط البيانات من جداول متعددة بناءً على علاقة مشتركة.
أنواع JOIN:
- INNER JOIN: يعيد السجلات المطابقة فقط
- LEFT JOIN: يعيد جميع السجلات من الجدول الأيسر
- RIGHT JOIN: يعيد جميع السجلات من الجدول الأيمن
- FULL OUTER JOIN: يعيد جميع السجلات من كلا الجدولين
INNER JOIN
INNER JOIN:
-- صيغة أساسية
SELECT u.name, o.order_date, o.total
FROM users u
INNER JOIN orders o ON u.id = o.user_id;
-- مع WHERE
SELECT u.name, o.order_date
FROM users u
INNER JOIN orders o ON u.id = o.user_id
WHERE o.total > 1000;
-- JOIN متعدد
SELECT u.name, o.order_date, p.product_name
FROM users u
INNER JOIN orders o ON u.id = o.user_id
INNER JOIN products p ON o.product_id = p.id;
LEFT JOIN و RIGHT JOIN
LEFT JOIN:
SELECT u.name, o.order_date
FROM users u
LEFT JOIN orders o ON u.id = o.user_id;
-- يعيد جميع المستخدمين حتى لو لم يكن لديهم طلبات
RIGHT JOIN:
SELECT u.name, o.order_date
FROM users u
RIGHT JOIN orders o ON u.id = o.user_id;
-- يعيد جميع الطلبات حتى لو لم يكن المستخدم موجوداً
FULL OUTER JOIN
FULL OUTER JOIN:
SELECT u.name, o.order_date
FROM users u
FULL OUTER JOIN orders o ON u.id = o.user_id;
-- يعيد جميع السجلات من كلا الجدولين
ملاحظة:
بعض قواعد البيانات لا تدعم FULL OUTER JOIN مباشرة، يمكن استخدام UNION مع LEFT و RIGHT JOIN.
الدوال التجميعية
الدوال التجميعية:
-- COUNT: عدد السجلات
SELECT COUNT(*) FROM users;
SELECT COUNT(DISTINCT city) FROM users;
-- SUM: مجموع القيم
SELECT SUM(total) FROM orders;
-- AVG: المتوسط
SELECT AVG(age) FROM users;
-- MAX: القيمة العظمى
SELECT MAX(price) FROM products;
-- MIN: القيمة الصغرى
SELECT MIN(price) FROM products;
GROUP BY
تجميع البيانات:
-- تجميع بسيط
SELECT city, COUNT(*)
FROM users
GROUP BY city;
-- تجميع مع دوال
SELECT city, AVG(age), COUNT(*)
FROM users
GROUP BY city;
-- تجميع متعدد
SELECT city, gender, COUNT(*)
FROM users
GROUP BY city, gender;
-- مع ORDER BY
SELECT city, COUNT(*) as count
FROM users
GROUP BY city
ORDER BY count DESC;
HAVING
HAVING Clause:
-- HAVING مع GROUP BY
SELECT city, COUNT(*) as count
FROM users
GROUP BY city
HAVING COUNT(*) > 10;
-- HAVING vs WHERE
SELECT city, AVG(age) as avg_age
FROM users
WHERE age > 18
GROUP BY city
HAVING AVG(age) > 25;
الفرق بين WHERE و HAVING:
- WHERE: يفلتر السجلات قبل التجميع
- HAVING: يفلتر المجموعات بعد التجميع
الدوال النصية
دوال النصوص:
-- UPPER و LOWER
SELECT UPPER(name) FROM users;
SELECT LOWER(email) FROM users;
-- SUBSTRING
SELECT SUBSTRING(name, 1, 5) FROM users;
SELECT SUBSTRING(email, 1, LOCATE('@', email) - 1) FROM users;
-- CONCAT
SELECT CONCAT(name, ' - ', email) FROM users;
-- LENGTH
SELECT name, LENGTH(name) FROM users;
-- TRIM
SELECT TRIM(' أحمد ') AS trimmed;
-- REPLACE
SELECT REPLACE(email, '@gmail.com', '@yahoo.com') FROM users;
الدوال التاريخية
دوال التاريخ:
-- NOW و CURRENT_DATE
SELECT NOW();
SELECT CURRENT_DATE;
SELECT CURRENT_TIME;
-- DATE_FORMAT
SELECT DATE_FORMAT(created_at, '%Y-%m-%d') FROM users;
-- DATEDIFF
SELECT DATEDIFF('2024-12-31', '2024-01-01') AS days;
-- DATE_ADD و DATE_SUB
SELECT DATE_ADD(created_at, INTERVAL 30 DAY) FROM users;
SELECT DATE_SUB(created_at, INTERVAL 1 MONTH) FROM users;
-- YEAR, MONTH, DAY
SELECT YEAR(created_at), MONTH(created_at), DAY(created_at) FROM users;
Subqueries
الاستعلامات الفرعية:
-- Subquery في WHERE
SELECT * FROM users
WHERE age > (SELECT AVG(age) FROM users);
-- Subquery في SELECT
SELECT name,
(SELECT COUNT(*) FROM orders WHERE user_id = users.id) AS order_count
FROM users;
-- Subquery في FROM
SELECT * FROM (
SELECT name, age FROM users WHERE age > 25
) AS filtered_users;
-- EXISTS
SELECT * FROM users u
WHERE EXISTS (
SELECT 1 FROM orders o WHERE o.user_id = u.id
);
UNION و UNION ALL
UNION:
-- UNION (يحذف التكرارات)
SELECT name FROM users
UNION
SELECT name FROM customers;
-- UNION ALL (يحتفظ بالتكرارات)
SELECT name FROM users
UNION ALL
SELECT name FROM customers;
-- مع ORDER BY
SELECT name FROM users
UNION
SELECT name FROM customers
ORDER BY name;
Constraints
القيود (Constraints):
-- PRIMARY KEY
CREATE TABLE users (
id INT PRIMARY KEY,
name VARCHAR(100)
);
-- FOREIGN KEY
CREATE TABLE orders (
id INT PRIMARY KEY,
user_id INT,
FOREIGN KEY (user_id) REFERENCES users(id)
);
-- UNIQUE
CREATE TABLE users (
id INT PRIMARY KEY,
email VARCHAR(100) UNIQUE
);
-- CHECK
CREATE TABLE users (
id INT PRIMARY KEY,
age INT CHECK (age >= 18)
);
-- NOT NULL
CREATE TABLE users (
id INT PRIMARY KEY,
name VARCHAR(100) NOT NULL
);
Indexes
الفهارس (Indexes):
-- إنشاء فهرس
CREATE INDEX idx_email ON users(email);
-- فهرس مركب
CREATE INDEX idx_city_age ON users(city, age);
-- فهرس فريد
CREATE UNIQUE INDEX idx_email_unique ON users(email);
-- حذف فهرس
DROP INDEX idx_email ON users;
-- عرض الفهارس
SHOW INDEXES FROM users;
فوائد الفهارس:
- تسريع عمليات البحث
- تحسين أداء JOIN
- تسريع ORDER BY
Views
العروض (Views):
-- إنشاء View
CREATE VIEW user_orders AS
SELECT u.name, o.order_date, o.total
FROM users u
INNER JOIN orders o ON u.id = o.user_id;
-- استخدام View
SELECT * FROM user_orders;
-- تحديث View
CREATE OR REPLACE VIEW user_orders AS
SELECT u.name, o.order_date, o.total, o.status
FROM users u
INNER JOIN orders o ON u.id = o.user_id;
-- حذف View
DROP VIEW user_orders;
Stored Procedures
الإجراءات المخزنة:
-- إنشاء Procedure
DELIMITER //
CREATE PROCEDURE GetUserOrders(IN user_id INT)
BEGIN
SELECT * FROM orders WHERE user_id = user_id;
END //
DELIMITER ;
-- استدعاء Procedure
CALL GetUserOrders(1);
-- Procedure مع معاملات متعددة
DELIMITER //
CREATE PROCEDURE AddUser(
IN p_name VARCHAR(100),
IN p_email VARCHAR(100)
)
BEGIN
INSERT INTO users (name, email) VALUES (p_name, p_email);
END //
DELIMITER ;
CALL AddUser('أحمد', 'ahmed@example.com');
Functions
الدوال المخصصة:
-- إنشاء Function
DELIMITER //
CREATE FUNCTION GetUserAge(user_id INT)
RETURNS INT
READS SQL DATA
BEGIN
DECLARE user_age INT;
SELECT age INTO user_age FROM users WHERE id = user_id;
RETURN user_age;
END //
DELIMITER ;
-- استخدام Function
SELECT GetUserAge(1) AS age;
-- Function مع معاملات
DELIMITER //
CREATE FUNCTION CalculateDiscount(price DECIMAL(10,2), discount_percent INT)
RETURNS DECIMAL(10,2)
DETERMINISTIC
BEGIN
RETURN price * (1 - discount_percent / 100);
END //
DELIMITER ;
SELECT CalculateDiscount(100, 20) AS final_price;
Triggers
المشغلات (Triggers):
-- Trigger قبل INSERT
DELIMITER //
CREATE TRIGGER before_user_insert
BEFORE INSERT ON users
FOR EACH ROW
BEGIN
SET NEW.created_at = NOW();
END //
DELIMITER ;
-- Trigger بعد UPDATE
DELIMITER //
CREATE TRIGGER after_order_update
AFTER UPDATE ON orders
FOR EACH ROW
BEGIN
INSERT INTO order_history (order_id, old_status, new_status, updated_at)
VALUES (NEW.id, OLD.status, NEW.status, NOW());
END //
DELIMITER ;
-- حذف Trigger
DROP TRIGGER before_user_insert;
مشروع قاعدة بيانات
نظام إدارة متجر:
-- إنشاء قاعدة البيانات
CREATE DATABASE store_db;
USE store_db;
-- جدول العملاء
CREATE TABLE customers (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(100) NOT NULL,
email VARCHAR(100) UNIQUE,
phone VARCHAR(20),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- جدول المنتجات
CREATE TABLE products (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(100) NOT NULL,
price DECIMAL(10,2) NOT NULL,
stock INT DEFAULT 0,
category_id INT
);
-- جدول الطلبات
CREATE TABLE orders (
id INT PRIMARY KEY AUTO_INCREMENT,
customer_id INT,
order_date DATE,
total DECIMAL(10,2),
status VARCHAR(20) DEFAULT 'pending',
FOREIGN KEY (customer_id) REFERENCES customers(id)
);
-- جدول تفاصيل الطلبات
CREATE TABLE order_items (
id INT PRIMARY KEY AUTO_INCREMENT,
order_id INT,
product_id INT,
quantity INT,
price DECIMAL(10,2),
FOREIGN KEY (order_id) REFERENCES orders(id),
FOREIGN KEY (product_id) REFERENCES products(id)
);
Best Practices
أفضل الممارسات:
- استخدم أسماء واضحة للجداول والأعمدة
- استخدم PRIMARY KEY لكل جدول
- استخدم FOREIGN KEY للعلاقات
- أضف فهارس على الأعمدة المستخدمة في WHERE و JOIN
- استخدم Transactions للعمليات المهمة
- احذف البيانات القديمة بانتظام
- استخدم Prepared Statements للحماية من SQL Injection
- احتفظ بنسخ احتياطية منتظمة
- استخدم EXPLAIN لتحليل أداء الاستعلامات
- تجنب SELECT * واستخدم أسماء الأعمدة
مشروع تطبيقي كامل
نظام إدارة مكتبة:
-- قاعدة البيانات
CREATE DATABASE library_db;
USE library_db;
-- جدول الكتب
CREATE TABLE books (
id INT PRIMARY KEY AUTO_INCREMENT,
title VARCHAR(200) NOT NULL,
author VARCHAR(100),
isbn VARCHAR(20) UNIQUE,
category VARCHAR(50),
published_year INT,
copies INT DEFAULT 1
);
-- جدول الأعضاء
CREATE TABLE members (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(100) NOT NULL,
email VARCHAR(100) UNIQUE,
phone VARCHAR(20),
membership_date DATE
);
-- جدول الإعارات
CREATE TABLE loans (
id INT PRIMARY KEY AUTO_INCREMENT,
member_id INT,
book_id INT,
loan_date DATE,
return_date DATE,
due_date DATE,
status VARCHAR(20) DEFAULT 'active',
FOREIGN KEY (member_id) REFERENCES members(id),
FOREIGN KEY (book_id) REFERENCES books(id)
);
-- View للإعارات النشطة
CREATE VIEW active_loans AS
SELECT m.name AS member_name,
b.title AS book_title,
l.loan_date,
l.due_date
FROM loans l
INNER JOIN members m ON l.member_id = m.id
INNER JOIN books b ON l.book_id = b.id
WHERE l.status = 'active';
-- Procedure لإضافة إعارة
DELIMITER //
CREATE PROCEDURE AddLoan(
IN p_member_id INT,
IN p_book_id INT
)
BEGIN
DECLARE available_copies INT;
SELECT copies INTO available_copies FROM books WHERE id = p_book_id;
IF available_copies > 0 THEN
INSERT INTO loans (member_id, book_id, loan_date, due_date)
VALUES (p_member_id, p_book_id, CURDATE(), DATE_ADD(CURDATE(), INTERVAL 14 DAY));
UPDATE books SET copies = copies - 1 WHERE id = p_book_id;
END IF;
END //
DELIMITER ;