引言

本文通过电商订单系统的实战案例,综合运用前面学到的SQL知识,实现各种复杂的业务查询。


一、表结构设计

1.1 核心表

-- 用户表
CREATE TABLE users (
    id INT PRIMARY KEY AUTO_INCREMENT,
    username VARCHAR(50) NOT NULL,
    email VARCHAR(100),
    phone VARCHAR(20),
    register_date DATE,
    level INT DEFAULT 1  -- 用户等级
);

-- 商品表
CREATE TABLE products (
    id INT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(200) NOT NULL,
    category VARCHAR(50),
    price DECIMAL(10,2),
    stock INT DEFAULT 0,
    sales INT DEFAULT 0
);

-- 订单表
CREATE TABLE orders (
    id INT PRIMARY KEY AUTO_INCREMENT,
    order_no VARCHAR(50) UNIQUE,
    user_id INT,
    total_amount DECIMAL(10,2),
    status VARCHAR(20),  -- pending, paid, shipped, completed, cancelled
    created_at DATETIME,
    paid_at DATETIME,
    FOREIGN KEY (user_id) REFERENCES users(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)
);

二、基础查询实战

2.1 用户订单查询

-- 查询用户的所有订单
SELECT u.username,
       o.order_no,
       o.total_amount,
       o.status,
       DATE_FORMAT(o.created_at, '%Y-%m-%d %H:%i') AS order_time
FROM users u
INNER JOIN orders o ON u.id = o.user_id
WHERE u.id = 1
ORDER BY o.created_at DESC;

2.2 订单详情查询

-- 查询订单明细(包含商品信息)
SELECT o.order_no,
       p.name AS product_name,
       oi.quantity,
       oi.price,
       oi.quantity * oi.price AS subtotal
FROM orders o
INNER JOIN order_items oi ON o.id = oi.order_id
INNER JOIN products p ON oi.product_id = p.id
WHERE o.order_no = 'ORD20241121000001';

三、统计分析实战

3.1 用户消费统计

-- 统计每个用户的消费情况
SELECT u.id,
       u.username,
       u.level,
       COUNT(o.id) AS order_count,
       IFNULL(SUM(o.total_amount), 0) AS total_spent,
       ROUND(AVG(o.total_amount), 2) AS avg_order_value,
       MAX(o.created_at) AS last_order_date
FROM users u
LEFT JOIN orders o ON u.id = o.user_id AND o.status = 'completed'
GROUP BY u.id, u.username, u.level
ORDER BY total_spent DESC;

3.2 商品销售排行

-- 销量Top 10商品
SELECT p.name,
       p.category,
       SUM(oi.quantity) AS total_sales,
       SUM(oi.quantity * oi.price) AS total_revenue
FROM products p
INNER JOIN order_items oi ON p.id = oi.product_id
INNER JOIN orders o ON oi.order_id = o.id
WHERE o.status = 'completed'
GROUP BY p.id, p.name, p.category
ORDER BY total_sales DESC
LIMIT 10;

3.3 按月销售统计

-- 每月销售额趋势
SELECT DATE_FORMAT(created_at, '%Y-%m') AS month,
       COUNT(*) AS order_count,
       COUNT(DISTINCT user_id) AS customer_count,
       SUM(total_amount) AS total_sales,
       ROUND(AVG(total_amount), 2) AS avg_order_value
FROM orders
WHERE status = 'completed'
  AND created_at >= DATE_SUB(NOW(), INTERVAL 12 MONTH)
GROUP BY DATE_FORMAT(created_at, '%Y-%m')
ORDER BY month;

四、高级查询实战

4.1 用户购买行为分析

-- 分析用户首次购买、最近购买、购买频次
SELECT u.username,
       u.register_date,
       MIN(o.created_at) AS first_order_date,
       MAX(o.created_at) AS last_order_date,
       COUNT(o.id) AS order_count,
       TIMESTAMPDIFF(DAY, u.register_date, MIN(o.created_at)) AS days_to_first_order,
       TIMESTAMPDIFF(DAY, MIN(o.created_at), MAX(o.created_at)) AS active_days
FROM users u
LEFT JOIN orders o ON u.id = o.user_id AND o.status = 'completed'
GROUP BY u.id, u.username, u.register_date
HAVING order_count > 0;

4.2 复购率分析

-- 计算复购率(购买2次及以上的用户占比)
SELECT
    COUNT(DISTINCT user_id) AS total_customers,
    COUNT(DISTINCT CASE WHEN order_count >= 2 THEN user_id END) AS repeat_customers,
    ROUND(COUNT(DISTINCT CASE WHEN order_count >= 2 THEN user_id END) * 100.0 / COUNT(DISTINCT user_id), 2) AS repeat_rate
FROM (
    SELECT user_id, COUNT(*) AS order_count
    FROM orders
    WHERE status = 'completed'
    GROUP BY user_id
) t;

4.3 客户分层(RFM模型)

-- RFM分析:Recency(最近购买)、Frequency(购买频次)、Monetary(消费金额)
SELECT user_id,
       username,
       DATEDIFF(NOW(), last_order_date) AS recency,
       order_count AS frequency,
       total_amount AS monetary,
       CASE
           WHEN DATEDIFF(NOW(), last_order_date) <= 30 AND order_count >= 5 AND total_amount >= 10000 THEN 'VIP客户'
           WHEN DATEDIFF(NOW(), last_order_date) <= 90 AND order_count >= 3 THEN '活跃客户'
           WHEN DATEDIFF(NOW(), last_order_date) > 180 THEN '流失客户'
           ELSE '普通客户'
       END AS customer_segment
FROM (
    SELECT u.id AS user_id,
           u.username,
           MAX(o.created_at) AS last_order_date,
           COUNT(o.id) AS order_count,
           SUM(o.total_amount) AS total_amount
    FROM users u
    LEFT JOIN orders o ON u.id = o.user_id AND o.status = 'completed'
    GROUP BY u.id, u.username
) t
ORDER BY monetary DESC;

五、复杂业务场景

5.1 购物车转化率

-- 统计下单未支付的订单(购物车)
SELECT
    COUNT(*) AS cart_count,
    SUM(CASE WHEN status = 'paid' THEN 1 ELSE 0 END) AS paid_count,
    ROUND(SUM(CASE WHEN status = 'paid' THEN 1 ELSE 0 END) * 100.0 / COUNT(*), 2) AS conversion_rate
FROM orders
WHERE created_at >= DATE_SUB(NOW(), INTERVAL 30 DAY);

5.2 商品关联分析

-- 查找经常一起购买的商品
SELECT p1.name AS product1,
       p2.name AS product2,
       COUNT(*) AS co_purchase_count
FROM order_items oi1
INNER JOIN order_items oi2 ON oi1.order_id = oi2.order_id AND oi1.product_id < oi2.product_id
INNER JOIN products p1 ON oi1.product_id = p1.id
INNER JOIN products p2 ON oi2.product_id = p2.id
GROUP BY p1.id, p1.name, p2.id, p2.name
ORDER BY co_purchase_count DESC
LIMIT 10;

5.3 库存预警

-- 库存不足且销售较好的商品
SELECT name,
       category,
       stock,
       sales,
       CASE
           WHEN stock = 0 THEN '缺货'
           WHEN stock < sales * 0.1 THEN '严重不足'
           WHEN stock < sales * 0.3 THEN '库存紧张'
           ELSE '正常'
       END AS stock_status
FROM products
WHERE stock < sales * 0.3
ORDER BY stock_status, sales DESC;

六、性能优化实战

6.1 创建索引

-- 为常用查询字段创建索引
CREATE INDEX idx_user_id ON orders(user_id);
CREATE INDEX idx_status ON orders(status);
CREATE INDEX idx_created_at ON orders(created_at);
CREATE INDEX idx_order_id ON order_items(order_id);
CREATE INDEX idx_product_id ON order_items(product_id);

-- 联合索引
CREATE INDEX idx_status_user_id ON orders(status, user_id);
CREATE INDEX idx_status_created_at ON orders(status, created_at);

6.2 创建视图简化查询

-- 用户订单统计视图
CREATE VIEW v_user_order_stats AS
SELECT u.id AS user_id,
       u.username,
       u.level,
       COUNT(o.id) AS order_count,
       IFNULL(SUM(o.total_amount), 0) AS total_spent,
       MAX(o.created_at) AS last_order_date
FROM users u
LEFT JOIN orders o ON u.id = o.user_id AND o.status = 'completed'
GROUP BY u.id, u.username, u.level;

-- 使用视图
SELECT * FROM v_user_order_stats WHERE total_spent > 10000;

七、总结

知识点回顾

本案例综合运用了:

  1. 多表连接:INNER JOIN、LEFT JOIN
  2. 聚合函数:COUNT、SUM、AVG、MAX、MIN
  3. 分组查询:GROUP BY、HAVING
  4. 窗口函数:ROW_NUMBER、RANK(可选)
  5. 子查询:嵌套查询、表子查询
  6. 条件表达式:CASE WHEN、IF
  7. 日期函数:DATE_FORMAT、DATEDIFF、TIMESTAMPDIFF
  8. 视图:简化复杂查询

实战技巧

  1. 先小范围测试:WHERE限定数据范围
  2. 分步构建查询:从简单到复杂
  3. 使用EXPLAIN:分析执行计划
  4. 创建合适索引:提升查询性能
  5. 使用视图封装:提高代码复用

本文字数:约3,500字 难度等级:⭐⭐⭐(SQL进阶综合)


恭喜!

至此,MySQL从入门到精通专题的第二阶段(SQL进阶篇)全部12篇文章已完成!

已完成内容

  • 第11篇:WHERE条件与运算符
  • 第12篇:ORDER BY与LIMIT
  • 第13篇:聚合函数
  • 第14篇:GROUP BY与HAVING
  • 第15篇:多表连接
  • 第16篇:子查询
  • 第17篇:UNION联合查询
  • 第18篇:窗口函数
  • 第19篇:字符串和日期函数
  • 第20篇:CASE WHEN条件表达式
  • 第21篇:视图
  • 第22篇:综合实战

掌握了这些知识,你已经具备了处理复杂SQL查询的能力!下一阶段将学习索引与优化,敬请期待!