MySQL: مشروع متكامل

آخر تحديث: 2026-08-26

حصلت «ShopEasy»، منصة التجارة الإلكترونية التي أسستها أليس، مؤخرًا على تمويل، وتحتاج الآن إلى الترقية من نظام إدارة يعتمد على برنامج «إكسل» إلى نظام «MySQL». وقد صممت أليس قاعدة بيانات كاملة للتجارة الإلكترونية من الصفر، تتألف من ثماني وحدات تغطي العملية بأكملها بدءًا من تسجيل المستخدمين وصولاً إلى تحليل البيانات. ومنذ إطلاقها، تعالج المنصة أكثر من 5,000 طلب يوميًا، ولا تزال قاعدة البيانات تعمل بثبات تام.

1. نظرة عامة على المشروع

(1) قائمة الوحدات والجداول

الوحدة الجدول الأساسي الوظائف الرئيسية
نظام المستخدمين المستخدمون / العناوين / سجلات المستخدمين التسجيل وتسجيل الدخول، وإدارة العناوين، ومراجعة العمليات
نظام المنتجات الفئات / المنتجات / صور_المنتجات تصنيف بمستويات غير محدودة، عمليات إنشاء وقراءة وقراءة وتعديل وحذف للمنتجات، صور متعددة
نظام الطلبات الطلبات / بنود_الطلب تقديم الطلب، خصم المخزون، تحديثات الحالة
سلة التسوق السلة إضافة، حذف، تعديل، عرض، دمج سلال التسوق
سجل الدفعات الدفعات حالة الدفع، المبالغ المستردة
الإحصائيات عدد المشاهدات المنتجات الأكثر مبيعًا، تصنيفات المبيعات، التقارير الشهرية
إدارة الأذونات مستخدمو MySQL للقراءة فقط/التطبيق/المسؤول
استراتيجية النسخ الاحتياطي mysqldump سياسة النسخ الاحتياطي اليومي والاحتفاظ بالبيانات

(2) مخطط ER الكامل

100%
erDiagram
    USERS ||--o{ ADDRESSES : has
    USERS ||--o{ ORDERS : places
    USERS ||--o{ CART : adds
    USERS ||--o{ USER_LOGS : generates
    CATEGORIES ||--o{ CATEGORIES : parent
    CATEGORIES ||--o{ PRODUCTS : contains
    PRODUCTS ||--o{ PRODUCT_IMAGES : has
    PRODUCTS ||--o{ ORDER_ITEMS : included_in
    PRODUCTS ||--o{ CART : added_to
    ORDERS ||--|{ ORDER_ITEMS : contains
    ORDERS ||--o| PAYMENTS : paid_by

    USERS {
        bigint id PK
        varchar username
        varchar email
        varchar password_hash
        varchar phone
        enum status
    }
    ADDRESSES {
        bigint id PK
        bigint user_id FK
        varchar receiver
        varchar phone
        varchar province
        varchar city
        varchar detail
        tinyint is_default
    }
    USER_LOGS {
        bigint id PK
        bigint user_id FK
        varchar action
        varchar ip_address
        datetime created_at
    }
    CATEGORIES {
        bigint id PK
        varchar name
        bigint parent_id FK
        int sort_order
    }
    PRODUCTS {
        bigint id PK
        varchar name
        decimal price
        int stock
        bigint category_id FK
        enum status
    }
    PRODUCT_IMAGES {
        bigint id PK
        bigint product_id FK
        varchar image_url
        tinyint is_main
        int sort_order
    }
    ORDERS {
        bigint id PK
        varchar order_no
        bigint user_id FK
        decimal total_amount
        enum status
    }
    ORDER_ITEMS {
        bigint id PK
        bigint order_id FK
        bigint product_id FK
        int quantity
        decimal price
    }
    CART {
        bigint id PK
        bigint user_id FK
        bigint product_id FK
        int quantity
    }
    PAYMENTS {
        bigint id PK
        bigint order_id FK
        varchar payment_no
        decimal amount
        enum method
        enum status
    }


2. تحليل المتطلبات وتصميم نموذج العلاقات (ER)

(1) تحليل المتطلبات

سير العمل الأساسي لنظام التجارة الإلكترونية: تسجيل المستخدم → تصفح المنتجات → إضافة المنتجات إلى سلة التسوق → تقديم الطلب → الدفع → الشحن → إتمام العملية. تتطلب كل خطوة جداول وقيودًا مناسبة.

(2) وصف العلاقة

العلاقة النوع الوصف
المستخدم → العنوان واحد إلى عدة يمكن أن يكون للمستخدم عدة عناوين شحن
المستخدم → الطلب علاقة «واحد إلى عدة» يمكن لمستخدم واحد تقديم عدة طلبات
الطلب → بند الطلب واحد إلى عدة يحتوي الطلب على عدة بنود
المنتج → بند الطلب علاقة «واحد إلى عدة» يمكن أن يظهر منتج واحد في عدة طلبات
الفئة → الفئة الإشارة الذاتية معرّف الفئة الأصلية (parent_id) لتنفيذ التصنيف ذي المستويات اللانهائية
الفئة → المنتج واحد إلى عدة عدة منتجات ضمن فئة واحدة
المنتج → الصورة واحد إلى عدة عدة صور لكل منتج
الطلب → الدفع فردي سجل دفع واحد لكل طلب

▶ مثال: إنشاء قاعدة بيانات

SQL
CREATE DATABASE IF NOT EXISTS ecommerce
CHARACTER SET utf8mb4
COLLATE utf8mb4_unicode_ci;

USE ecommerce;
▶ جرّب الكود

3. نظام المستخدم

(1) تصميم بنية الجدول

SQL
CREATE TABLE users (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    username VARCHAR(50) NOT NULL UNIQUE,
    email VARCHAR(100) NOT NULL UNIQUE,
    password_hash VARCHAR(255) NOT NULL,
    phone VARCHAR(20),
    avatar VARCHAR(255) DEFAULT '/images/default_avatar.png',
    status ENUM('active', 'inactive', 'banned') DEFAULT 'active',
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    INDEX idx_users_phone (phone),
    INDEX idx_users_status (status)
) ENGINE=InnoDB COMMENT='User Table';

CREATE TABLE addresses (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    user_id BIGINT NOT NULL,
    receiver VARCHAR(50) NOT NULL,
    phone VARCHAR(20) NOT NULL,
    province VARCHAR(30) NOT NULL,
    city VARCHAR(30) NOT NULL,
    district VARCHAR(30) NOT NULL,
    detail VARCHAR(200) NOT NULL,
    is_default TINYINT(1) DEFAULT 0,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    INDEX idx_addresses_user (user_id),
    FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
) ENGINE=InnoDB COMMENT='List of Shipping Addresses';

CREATE TABLE user_logs (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    user_id BIGINT,
    action VARCHAR(50) NOT NULL,
    ip_address VARCHAR(45),
    user_agent VARCHAR(255),
    detail JSON,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    INDEX idx_logs_user (user_id),
    INDEX idx_logs_action (action),
    INDEX idx_logs_created (created_at)
) ENGINE=InnoDB COMMENT='User Activity Log Table';

(2) التسجيل والتحقق من تسجيل الدخول

▶ مثال: تسجيل المستخدم

SQL
INSERT INTO users (username, email, password_hash, phone)
VALUES (
    'alice',
    'alice@example.com',
    SHA2('MySecret123!', 256),
    '13800138000'
);

INSERT INTO users (username, email, password_hash, phone)
VALUES (
    'bob_dev',
    'bob@example.com',
    SHA2('BobPass456!', 256),
    '13900139000'
);
▶ جرّب الكود

▶ مثال: مصادقة تسجيل الدخول

SQL
SELECT id, username, status FROM users
WHERE email = 'alice@example.com'
  AND password_hash = SHA2('MySecret123!', 256)
  AND status = 'active';
▶ جرّب الكود

▶ مثال: إدارة العناوين

SQL
INSERT INTO addresses (user_id, receiver, phone, province, city, district, detail, is_default)
VALUES (1, 'Alice', '13800138000', 'Guangdong', 'Shenzhen', 'Nanshan', 'Tech Park Bldg 5 Room 301', 1);

INSERT INTO addresses (user_id, receiver, phone, province, city, district, detail, is_default)
VALUES (1, 'Alice', '13800138000', 'Beijing', 'Beijing', 'Haidian', 'Zhongguancun St 88', 0);

SELECT * FROM addresses WHERE user_id = 1 ORDER BY is_default DESC;
▶ جرّب الكود

▶ مثال: سجل أنشطة المستخدم

SQL
INSERT INTO user_logs (user_id, action, ip_address, user_agent, detail)
VALUES (1, 'LOGIN', '192.168.1.100', 'Chrome/120', '{"method": "email"}');

INSERT INTO user_logs (user_id, action, ip_address, detail)
VALUES (1, 'UPDATE_ADDRESS', '192.168.1.100', '{"address_id": 1}');

SELECT action, COUNT(*) AS cnt
FROM user_logs
WHERE user_id = 1
GROUP BY action
ORDER BY cnt DESC;
▶ جرّب الكود

4. نظام المنتجات

(1) التصنيف ذو المستويات اللانهائية

SQL
CREATE TABLE categories (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(50) NOT NULL,
    parent_id BIGINT DEFAULT 0,
    sort_order INT DEFAULT 0,
    icon VARCHAR(255),
    is_visible TINYINT(1) DEFAULT 1,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    INDEX idx_categories_parent (parent_id),
    INDEX idx_categories_sort (sort_order)
) ENGINE=InnoDB COMMENT='Product Category Table';

▶ مثال: البيانات التصنيفية

SQL
INSERT INTO categories (id, name, parent_id, sort_order) VALUES
(1, 'Electronics', 0, 1),
(2, 'Clothing', 0, 2),
(3, 'Home & Living', 0, 3),
(4, 'Smartphones', 1, 1),
(5, 'Laptops', 1, 2),
(6, 'Men', 2, 1),
(7, 'Women', 2, 2),
(8, 'Kitchen', 3, 1);

SELECT c1.name AS category, c2.name AS sub_category
FROM categories c1
LEFT JOIN categories c2 ON c1.id = c2.parent_id
WHERE c1.parent_id = 0
ORDER BY c1.sort_order, c2.sort_order;
▶ جرّب الكود

(2) المنتجات والصور

SQL
CREATE TABLE products (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(200) NOT NULL,
    description TEXT,
    price DECIMAL(10,2) NOT NULL CHECK (price > 0),
    original_price DECIMAL(10,2),
    stock INT NOT NULL DEFAULT 0 CHECK (stock >= 0),
    sales INT NOT NULL DEFAULT 0,
    category_id BIGINT NOT NULL,
    status ENUM('active', 'inactive', 'sold_out') DEFAULT 'active',
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    INDEX idx_products_category (category_id),
    INDEX idx_products_status (status),
    INDEX idx_products_sales (sales DESC),
    INDEX idx_products_created (created_at DESC),
    FOREIGN KEY (category_id) REFERENCES categories(id)
) ENGINE=InnoDB COMMENT='Product List';

CREATE TABLE product_images (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    product_id BIGINT NOT NULL,
    image_url VARCHAR(255) NOT NULL,
    is_main TINYINT(1) DEFAULT 0,
    sort_order INT DEFAULT 0,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    INDEX idx_images_product (product_id),
    FOREIGN KEY (product_id) REFERENCES products(id) ON DELETE CASCADE
) ENGINE=InnoDB COMMENT='Product Image Table';

▶ مثال: عمليات CRUD للمنتج

SQL
INSERT INTO products (name, description, price, original_price, stock, category_id, status)
VALUES
('Galaxy S24 Ultra', 'Samsung flagship 2024', 1299.99, 1399.99, 200, 4, 'active'),
('MacBook Pro 16', 'Apple M3 Max laptop', 2499.00, 2499.00, 50, 5, 'active'),
('Cotton T-Shirt', 'Pure cotton casual tee', 29.99, 39.99, 500, 6, 'active'),
('Smart Rice Cooker', 'IoT enabled cooker', 89.99, 119.99, 300, 8, 'active');

INSERT INTO product_images (product_id, image_url, is_main, sort_order) VALUES
(1, '/images/galaxy_s24_1.jpg', 1, 1),
(1, '/images/galaxy_s24_2.jpg', 0, 2),
(2, '/images/macbook_pro_1.jpg', 1, 1),
(3, '/images/tshirt_white.jpg', 1, 1),
(4, '/images/cooker_main.jpg', 1, 1);

SELECT p.id, p.name, p.price, pi.image_url
FROM products p
LEFT JOIN product_images pi ON p.id = pi.product_id AND pi.is_main = 1
WHERE p.category_id = 4 AND p.status = 'active'
ORDER BY p.sales DESC
LIMIT 10;

UPDATE products SET stock = stock - 1, sales = sales + 1 WHERE id = 1;

UPDATE products SET status = 'sold_out' WHERE stock <= 0;
▶ جرّب الكود

5. سلة التسوق

(1) بنية الجدول

SQL
CREATE TABLE cart (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    user_id BIGINT NOT NULL,
    product_id BIGINT NOT NULL,
    quantity INT NOT NULL DEFAULT 1 CHECK (quantity > 0),
    selected TINYINT(1) DEFAULT 1,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    UNIQUE KEY uk_cart_user_product (user_id, product_id),
    INDEX idx_cart_user (user_id),
    FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
    FOREIGN KEY (product_id) REFERENCES products(id) ON DELETE CASCADE
) ENGINE=InnoDB COMMENT='Shopping Cart Table';

▶ مثال: عمليات CRUD لعربة التسوق

SQL
INSERT INTO cart (user_id, product_id, quantity) VALUES (1, 1, 2);

INSERT INTO cart (user_id, product_id, quantity) VALUES (1, 2, 1)
ON DUPLICATE KEY UPDATE quantity = quantity + 1;

UPDATE cart SET quantity = 3 WHERE user_id = 1 AND product_id = 1;

DELETE FROM cart WHERE user_id = 1 AND product_id = 2;

SELECT c.product_id, p.name, p.price, c.quantity,
       p.price * c.quantity AS subtotal
FROM cart c
JOIN products p ON c.product_id = p.id
WHERE c.user_id = 1 AND c.selected = 1;
▶ جرّب الكود

▶ مثال: دمج عربات التسوق

SQL
DELIMITER //
CREATE PROCEDURE sp_merge_cart(
    IN p_source_user_id BIGINT,
    IN p_target_user_id BIGINT
)
BEGIN
    INSERT INTO cart (user_id, product_id, quantity, selected)
    SELECT p_target_user_id, product_id, quantity, selected
    FROM cart
    WHERE user_id = p_source_user_id
    ON DUPLICATE KEY UPDATE
        quantity = cart.quantity + VALUES(quantity),
        selected = 1;

    DELETE FROM cart WHERE user_id = p_source_user_id;
END //
DELIMITER ;

CALL sp_merge_cart(2, 1);
▶ جرّب الكود

6. نظام الطلبات

(1) الطلبات وبنود الطلبات

SQL
CREATE TABLE orders (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    order_no VARCHAR(32) NOT NULL UNIQUE,
    user_id BIGINT NOT NULL,
    total_amount DECIMAL(12,2) NOT NULL DEFAULT 0,
    pay_amount DECIMAL(12,2) DEFAULT NULL,
    status ENUM('pending', 'paid', 'shipped', 'completed', 'cancelled', 'refunded')
        DEFAULT 'pending',
    receiver VARCHAR(50) NOT NULL,
    receiver_phone VARCHAR(20) NOT NULL,
    shipping_address TEXT NOT NULL,
    remark VARCHAR(500),
    paid_at DATETIME DEFAULT NULL,
    shipped_at DATETIME DEFAULT NULL,
    completed_at DATETIME DEFAULT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    INDEX idx_orders_user (user_id),
    INDEX idx_orders_status (status),
    INDEX idx_orders_created (created_at DESC),
    INDEX idx_orders_order_no (order_no),
    FOREIGN KEY (user_id) REFERENCES users(id)
) ENGINE=InnoDB COMMENT='Orders Table';

CREATE TABLE order_items (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    order_id BIGINT NOT NULL,
    product_id BIGINT NOT NULL,
    product_name VARCHAR(200) NOT NULL,
    quantity INT NOT NULL CHECK (quantity > 0),
    price DECIMAL(10,2) NOT NULL,
    subtotal DECIMAL(12,2) GENERATED ALWAYS AS (quantity * price) STORED,
    INDEX idx_items_order (order_id),
    INDEX idx_items_product (product_id),
    FOREIGN KEY (order_id) REFERENCES orders(id) ON DELETE CASCADE,
    FOREIGN KEY (product_id) REFERENCES products(id)
) ENGINE=InnoDB COMMENT='Order Details';

(2) التغييرات في حالة الطلب

الحالة الحالية الحالات المحتملة شروط التشغيل
قيد الانتظار مدفوع تم الدفع بنجاح
قيد الانتظار ملغى ملغى من قبل المستخدم/غير مدفوع بسبب انتهاء المهلة
مدفوع تم الشحن تم الشحن بواسطة البائع
مدفوع تم استرداده طلب استرداد
تم الشحن اكتمل أكد المشتري استلامه
مكتمل تم استرداد المبلغ استرداد المبلغ بعد البيع

▶ مثال: الإجراء المخزّن لتقديم الطلبات (بما في ذلك خصم المخزون + المعاملة)

SQL 📖 للعرض فقط
DELIMITER //
CREATE PROCEDURE sp_create_order(
    IN p_user_id BIGINT,
    IN p_receiver VARCHAR(50),
    IN p_phone VARCHAR(20),
    IN p_address TEXT,
    IN p_remark VARCHAR(500)
)
BEGIN
    DECLARE v_order_no VARCHAR(32);
    DECLARE v_total DECIMAL(12,2) DEFAULT 0;
    DECLARE v_cart_item INT;
    DECLARE v_product_id BIGINT;
    DECLARE v_quantity INT;
    DECLARE v_price DECIMAL(10,2);
    DECLARE v_stock INT;
    DECLARE v_name VARCHAR(200);
    DECLARE done INT DEFAULT 0;
    DECLARE cart_cursor CURSOR FOR
        SELECT c.product_id, c.quantity, p.price, p.name, p.stock
        FROM cart c
        JOIN products p ON c.product_id = p.id
        WHERE c.user_id = p_user_id AND p.status = 'active'
        FOR UPDATE;
    DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;

    START TRANSACTION;

    SET v_order_no = CONCAT(
        'ORD', DATE_FORMAT(NOW(), '%Y%m%d%H%i%s'),
        LPAD(FLOOR(RAND() * 10000), 4, '0')
    );

    INSERT INTO orders (order_no, user_id, receiver, receiver_phone, shipping_address, remark)
    VALUES (v_order_no, p_user_id, p_receiver, p_phone, p_address, p_remark);

    OPEN cart_cursor;
    read_loop: LOOP
        FETCH cart_cursor INTO v_product_id, v_quantity, v_price, v_name, v_stock;
        IF done THEN LEAVE read_loop; END IF;

        IF v_stock < v_quantity THEN
            ROLLBACK;
            SIGNAL SQLSTATE '45000'
                SET MESSAGE_TEXT = CONCAT('Insufficient stock for: ', v_name);
        END IF;

        INSERT INTO order_items (order_id, product_id, product_name, quantity, price)
        VALUES (LAST_INSERT_ID(), v_product_id, v_name, v_quantity, v_price);

        UPDATE products
        SET stock = stock - v_quantity, sales = sales + v_quantity
        WHERE id = v_product_id;

        SET v_total = v_total + v_quantity * v_price;
    END LOOP;
    CLOSE cart_cursor;

    UPDATE orders SET total_amount = v_total WHERE order_no = v_order_no;
    DELETE FROM cart WHERE user_id = p_user_id;

    COMMIT;
END //
DELIMITER ;
54 سطر من الكود المنطقي (تجاوز الحد 40, للعرض فقط)

▶ مثال: تقديم طلب

SQL
CALL sp_create_order(
    1,
    'Alice',
    '13800138000',
    'Tech Park Bldg 5 Room 301, Nanshan, Shenzhen, Guangdong',
    'Please deliver before noon'
);
▶ جرّب الكود

▶ مثال: تحديث حالة الطلب

SQL
UPDATE orders SET status = 'paid', pay_amount = total_amount, paid_at = NOW()
WHERE id = 1 AND status = 'pending';

UPDATE orders SET status = 'shipped', shipped_at = NOW()
WHERE id = 1 AND status = 'paid';

UPDATE orders SET status = 'completed', completed_at = NOW()
WHERE id = 1 AND status = 'shipped';
▶ جرّب الكود

7. سجل الدفعات

(1) بنية الجدول

SQL
CREATE TABLE payments (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    order_id BIGINT NOT NULL,
    payment_no VARCHAR(64) NOT NULL UNIQUE,
    amount DECIMAL(12,2) NOT NULL,
    method ENUM('credit_card', 'debit_card', 'paypal', 'bank_transfer') NOT NULL,
    status ENUM('pending', 'success', 'failed', 'refunded') DEFAULT 'pending',
    paid_at DATETIME DEFAULT NULL,
    refund_no VARCHAR(64) DEFAULT NULL,
    refund_amount DECIMAL(12,2) DEFAULT NULL,
    refund_reason VARCHAR(500) DEFAULT NULL,
    refunded_at DATETIME DEFAULT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    INDEX idx_payments_order (order_id),
    INDEX idx_payments_no (payment_no),
    INDEX idx_payments_status (status),
    FOREIGN KEY (order_id) REFERENCES orders(id)
) ENGINE=InnoDB COMMENT='Payment History Report';

(2) تطور حالة الدفع

الحالة الحالية الحالات المحتملة شروط التشغيل
قيد الانتظار نجاح نجاح استدعاء بوابة الدفع
قيد الانتظار فشل فشل استدعاء بوابة الدفع
ناجح تم استرداد المبلغ بدأ إجراء الاسترداد

▶ مثال: معاملة دفع

SQL
INSERT INTO payments (order_id, payment_no, amount, method, status, paid_at)
VALUES (1, CONCAT('PAY', DATE_FORMAT(NOW(), '%Y%m%d%H%i%s'), LPAD(FLOOR(RAND()*10000),4,'0')),
        3899.97, 'credit_card', 'success', NOW());

UPDATE orders SET status = 'paid', pay_amount = 3899.97, paid_at = NOW()
WHERE id = 1 AND status = 'pending';

SELECT p.payment_no, p.amount, p.method, p.status, p.paid_at
FROM payments p
WHERE p.order_id = 1;
▶ جرّب الكود

▶ مثال: عملية استرداد الأموال

SQL
UPDATE payments
SET status = 'refunded',
    refund_no = CONCAT('REF', DATE_FORMAT(NOW(), '%Y%m%d%H%i%s'), LPAD(FLOOR(RAND()*10000),4,'0')),
    refund_amount = 3899.97,
    refund_reason = 'Defective product',
    refunded_at = NOW()
WHERE order_id = 1 AND status = 'success';

UPDATE orders SET status = 'refunded' WHERE id = 1;
▶ جرّب الكود

8. عرض إحصائيات البيانات

▶ مثال: عرض المنتجات الأكثر مبيعًا

SQL
CREATE OR REPLACE VIEW v_hot_products AS
SELECT
    p.id,
    p.name,
    p.price,
    c.name AS category,
    p.sales,
    IFNULL(SUM(oi.quantity), 0) AS order_sold,
    IFNULL(SUM(oi.quantity * oi.price), 0) AS revenue
FROM products p
LEFT JOIN order_items oi ON p.id = oi.product_id
LEFT JOIN categories c ON p.category_id = c.id
WHERE p.status = 'active'
GROUP BY p.id, p.name, p.price, c.name, p.sales
ORDER BY p.sales DESC;

SELECT * FROM v_hot_products LIMIT 10;
▶ جرّب الكود

▶ مثال: تصنيفات إنفاق المستخدمين

SQL
CREATE OR REPLACE VIEW v_user_ranking AS
SELECT
    u.id,
    u.username,
    COUNT(DISTINCT o.id) AS total_orders,
    IFNULL(SUM(o.total_amount), 0) AS total_spent,
    IFNULL(AVG(o.total_amount), 0) AS avg_order_amount,
    MAX(o.created_at) AS last_order_time
FROM users u
LEFT JOIN orders o ON u.id = o.user_id AND o.status != 'cancelled'
GROUP BY u.id, u.username
ORDER BY total_spent DESC;

SELECT * FROM v_user_ranking LIMIT 20;
▶ جرّب الكود

▶ مثال: تقرير المبيعات الشهري

SQL
CREATE OR REPLACE VIEW v_monthly_sales AS
SELECT
    DATE_FORMAT(o.created_at, '%Y-%m') AS month,
    COUNT(o.id) AS order_count,
    SUM(o.total_amount) AS revenue,
    SUM(CASE WHEN o.status = 'completed' THEN 1 ELSE 0 END) AS completed,
    SUM(CASE WHEN o.status = 'cancelled' THEN 1 ELSE 0 END) AS cancelled,
    SUM(CASE WHEN o.status = 'refunded' THEN 1 ELSE 0 END) AS refunded,
    ROUND(
        SUM(CASE WHEN o.status = 'completed' THEN 1 ELSE 0 END) / COUNT(o.id) * 100,
        2
    ) AS completion_rate
FROM orders o
GROUP BY DATE_FORMAT(o.created_at, '%Y-%m')
ORDER BY month DESC;

SELECT * FROM v_monthly_sales;
▶ جرّب الكود

9. إدارة الأذونات وسياسات النسخ الاحتياطي

(1) مصفوفة الأذونات

المستخدم SELECT INSERT UPDATE DELETE EXECUTE ALTER DROP CREATE
ecom_readonly ن - - - - - - -
ecom_app ن ن ن - ن - - -
ecom_admin ن ن ن ن ن ن ن ن

▶ مثال: إنشاء مستخدم مع أذونات

SQL
CREATE USER 'ecom_readonly'@'%' IDENTIFIED BY 'ReadOnly2024!';
GRANT SELECT ON ecommerce.* TO 'ecom_readonly'@'%';

CREATE USER 'ecom_app'@'%' IDENTIFIED BY 'AppUser2024!';
GRANT SELECT, INSERT, UPDATE ON ecommerce.* TO 'ecom_app'@'%';
GRANT EXECUTE ON ecommerce.* TO 'ecom_app'@'%';

CREATE USER 'ecom_admin'@'127.0.0.1' IDENTIFIED BY 'Admin2024!Secure';
GRANT ALL PRIVILEGES ON ecommerce.* TO 'ecom_admin'@'127.0.0.1';

FLUSH PRIVILEGES;

SHOW GRANTS FOR 'ecom_app'@'%';
▶ جرّب الكود

(2) استراتيجية النسخ الاحتياطي

نوع النسخ الاحتياطي التكرار مدة الاحتفاظ الأداة
نسخ احتياطي كامل يوميًا في الساعة 02:00 30 يومًا mysqldump
السجل الثنائي في الوقت الفعلي 7 أيام binlog
النسخ الاحتياطي لهيكل الجداول كل يوم أحد 90 يومًا mysqldump --no-data

▶ مثال: برنامج نصي للنسخ الاحتياطي

BASH
#!/bin/bash
BACKUP_DIR="/backup/mysql"
DB_NAME="ecommerce"
DB_USER="ecom_admin"
DATE=$(date +%Y%m%d_%H%M%S)

mkdir -p $BACKUP_DIR

mysqldump -u $DB_USER -pAdmin2024!Secure \
    --single-transaction \
    --routines \
    --triggers \
    $DB_NAME | gzip > ${BACKUP_DIR}/${DB_NAME}_full_${DATE}.sql.gz

mysqldump -u $DB_USER -pAdmin2024!Secure \
    --no-data \
    $DB_NAME | gzip > ${BACKUP_DIR}/${DB_NAME}_schema_${DATE}.sql.gz

find $BACKUP_DIR -name "${DB_NAME}_full_*.sql.gz" -mtime +30 -delete
find $BACKUP_DIR -name "${DB_NAME}_schema_*.sql.gz" -mtime +90 -delete

echo "Backup completed: ${DATE}"

▶ مثال: استعادة البيانات

BASH
gunzip < /backup/mysql/ecommerce_full_20240115_020000.sql.gz | mysql -u ecom_admin -p ecommerce


10. قائمة مراجعة لتصميم الفهرس

الجدول اسم الفهرس العمود النوع الغرض
المستخدمون idx_users_phone رقم الهاتف BTREE تسجيل الدخول برقم الهاتف
المستخدمون idx_users_status الحالة BTREE تصفية الحالة
العناوين idx_addresses_user user_id BTREE قائمة عناوين المستخدم
الفئات idx_categories_parent معرّف الفئة الأم BTREE البحث عن الفئات الفرعية
المنتجات idx_products_category category_id BTREE قائمة منتجات الفئة
المنتجات idx_products_status الحالة BTREE التصفية حسب حالة الإدراج
المنتجات idx_products_sales المبيعات (ترتيب تنازلي) BTREE الترتيب حسب الأكثر مبيعًا
المنتجات idx_products_created created_at DESC BTREE فرز المنتجات الجديدة
الطلبات idx_orders_user user_id BTREE قائمة طلبات المستخدم
الطلبات idx_orders_status الحالة BTREE تصفية الحالة
أوامر idx_orders_created created_at DESC BTREE الترتيب الزمني
أوامر idx_orders_order_no رقم_الأمر فريد البحث عن رقم الأمر
عناصر_الطلب ترتيب_عناصر_الطلب رقم_الطلب BTREE تفاصيل_الطلب
عناصر_الطلب فهرس_عناصر_المنتج معرّف_المنتج BTREE إحصائيات مبيعات المنتج
cart uk_cart_user_product user_id,product_id UNIQUE إزالة التكرارات من سلة التسوق
المدفوعات idx_payments_order order_id BTREE استعلام عن مدفوعات الطلب
المدفوعات idx_payments_status الحالة BTREE تصفية حالة الدفع
سجلات_المستخدم سجلات_المستخدم_idx معرّف_المستخدم BTREE سجلات المستخدم
سجلات_المستخدم سجلات_الفهرس_المنشأة تاريخ_الإنشاء BTREE استعلام النطاق الزمني


❓ أسئلة شائعة

س كيف يمكنك ضمان تفرد أرقام الطلبات؟
ج عن طريق ربط الطابع الزمني برقم عشوائي (على سبيل المثال، ORD202401151430520387)، أو باستخدام معرّف UUID، أو بالاعتماد على تسلسل التزايد التلقائي في Redis. قم بإنشاء رقم الطلب على النحو التالي: CONCAT + DATE_FORMAT + RAND أثناء عملية تخزين الطلب، واستخدم قيد UNIQUE كخيار احتياطي.
س ماذا أفعل في حالة بيع كمية من المخزون تفوق الكمية المتوفرة؟
ج هناك طريقتان — بالنسبة للتأمين المتشائم، استخدم SELECT ... FOR UPDATE لتأمين صف المخزون وخصم الكمية ضمن المعاملة؛ أما بالنسبة للتأمين المتفائل، فاستخدم الإصدار UPDATE products SET stock = stock - N, version = version + 1 WHERE id = X AND version = V. في حالات التزامن العالي، نوصي باستخدام التأمين المتفائل مع Redis لإجراء الخصم المسبق.
س هل جدول الطلبات كبير جدًّا؟
ج ثلاث استراتيجيات — 1. تقسيم الجدول حسب الشهر إلى orders_202401 وorders_202402؛ 2. استخدام تقسيم MySQL PARTITION BY RANGE (TO_DAYS(created_at))؛ 3. فصل البيانات النشطة عن غير النشطة؛ وأرشفة الطلبات التي مضى عليها أكثر من عام في جدول تاريخي. إضافة شرط زمني إلى الاستعلامات لاستهداف القسم الصحيح.
س كيف يمكن تصميم بنية تصنيف ذات مستويات لا نهائية؟
ج استخدم parent_id كمفتاح خارجي ذاتي الإشارة، مع الفئة ذات المستوى الأعلى parent_id = 0. وقم بالاستعلام عن الشجرة الفرعية باستخدام تعبير CTE متكرر: WITH RECURSIVE cat_tree AS (SELECT * FROM categories WHERE id = 1 UNION ALL SELECT c.* FROM categories c JOIN cat_tree ct ON c.parent_id = ct.id) SELECT * FROM cat_tree;
س هل يتم تخزين عربات التسوق في قاعدة البيانات أم في Redis؟
ج بالنسبة للمستخدمين المسجلين الدخول، يتم تخزين عربات التسوق في قاعدة البيانات (لضمان استمرارية البيانات) ويتم تخزينها مؤقتًا في Redis (لتسريع عمليات القراءة/الكتابة). أما بالنسبة للمستخدمين غير المسجلين الدخول، فتُخزَّن عربات التسوق في Redis أو LocalStorage وتُدمج في قاعدة البيانات عند تسجيل الدخول. ونظرًا لتكرار عمليات عربات التسوق، تساعد Redis في تقليل الحمل على قاعدة البيانات.
س لماذا نستخدم SHA-2 بدلاً من MD5 للكلمات المرور؟
ج ثبت أن MD5 عرضة لظاهرة التداخل ويمكن اختراقها بسهولة باستخدام جداول قوس قزح. ويُعد SHA-2-256 أكثر أمانًا؛ وفي بيئات التشغيل الفعلية، نوصي باستخدام bcrypt أو Argon2، اللذين يتضمنان قيم «الملح» (salt) ويسمحان بتعديل التكلفة الحسابية.
س لماذا يتضمن order_items نسخة مكررة من product_name؟
ج قد تتغير أسماء المنتجات، لكن لقطة الطلب يجب أن تحتفظ بالمعلومات كما كانت وقت تقديم الطلب. وهذا مثال كلاسيكي على «مكافحة التطبيع» — أي التضحية بقدر ضئيل من مساحة التخزين مقابل ضمان اتساق البيانات، وذلك لمنع عرض الطلبات السابقة بشكل غير صحيح بعد إعادة تسمية المنتج.

📖 ملخص


📝 تمارين

  1. سؤال أساسي (مستوى الصعوبة: ⭐): أنشئ قاعدة بيانات للتجارة الإلكترونية، واكتب عبارات SQL لإنشاء الجداول الأربعة (المستخدمون، والعناوين، والفئات، والمنتجات)، وأدخل بيانات اختبارية.

  2. تمرين متقدم (مستوى الصعوبة ⭐⭐): قم بتنفيذ ميزة سلة التسوق بالكامل — أنشئ الجداول، واكتب استعلامات SQL لعمليات CRUD، واكتب الإجراء المخزن sp_merge_cart، واستخدم ON DUPLICATE KEY UPDATE للتعامل مع الإضافات المكررة إلى سلة التسوق.

  3. تمرين عملي (مستوى الصعوبة: ⭐⭐⭐): قم بتنفيذ الإجراء المخزن الخاص بتقديم الطلب sp_create_order. يجب أن يتضمن الإجراء تكرارًا عبر سلة التسوق، والتحقق من المخزون (FOR UPDATE)، وإدراج الطلب وتفاصيله، وخصم الكمية من المخزون، ومسح سلة التسوق. يجب أن تكون العملية بأكملها محاطة بمعاملة؛ وإذا كان المخزون غير كافٍ، فقم بإلغاء المعاملة وإصدار رسالة خطأ.

  4. سؤال تحليلي (مستوى الصعوبة: ⭐⭐⭐): قم بإنشاء ثلاث طرق عرض إحصائية (المنتجات الأكثر مبيعًا / تصنيفات إنفاق المستخدمين / تقرير المبيعات الشهري)، واكتب استعلامات للتحقق من نتائج طرق العرض هذه، وقم بتحليل مشكلات الأداء التي تواجه طرق العرض هذه عندما يتجاوز جدول الطلبات 10 ملايين صف، مع اقتراح حلول للتحسين.

  5. التحدي (مستوى الصعوبة: ⭐⭐⭐⭐): قم بنشر قاعدة بيانات التجارة الإلكترونية ShopEasy بالكامل — بما في ذلك إنشاء قاعدة البيانات والجداول، والفهارس، والإجراءات المخزنة، وطرق العرض، وأذونات المستخدمين (للقراءة فقط/التطبيق/الإدارة) — إلى جانب برنامج نصي لإجراء نسخ احتياطي يومي. بالإضافة إلى ذلك، اكتب وثيقة نشر من 200 حرف توضح خطوات الإعداد الأولي والنقاط الرئيسية للعمليات اليومية والصيانة.

Web-Tutorial.com

فريق Web-Tutorial التقني

منصة دروس برمجية يديرها عدة مطورين. كل درس يتم كتابته ومراجعته بواسطة مطورين متخصصين في المجال. نعمل على ضمان دقة وموثوقية المحتوى — إذا لاحظت أي مشكلة، فيرجى إخبارنا.

100%