MySQL项目开发 (2)

一:简介

1:项目概述

电商数据仓库分析平台

2:核心人物

通过SQL开发,将原始订单数据转化为可供业务部门直接使用的分析报表。

3:项目价值

这类项目是数据开发岗位的核心工作,能全面提升你处理真实业务需求、优化复杂查询的能力。

二:项目需求与开发路线图

整个项目分为四个阶段,每个阶段对应一组具体的开发需求和需要掌握的SQL技能:

三:开发阶段

3.1: 开发阶段分析

阶段 开发需求 核心SQL技能与产出
阶段一:数据准备与探查 1. 创建原始订单表、用户表、商品表 2. 生成并探查模拟数据,了解数据全貌和质量 CREATE TABLE, INSERT, SELECT基础查询, COUNT, DISTINCT, WHERE过滤
阶段二:核心数据模型开发 1. 创建"用户订单宽表",聚合用户级别的关键指标 2. 创建"商品销售聚合表",分析商品表现 JOIN多表连接, GROUP BY聚合, SUM, AVG, CASE WHEN条件逻辑, CREATE VIEW
阶段三:复杂业务指标开发 1. 计算用户复购率与留存率 2. 分析商品销售关联性(哪些商品常被一起购买) 窗口函数(ROW_NUMBER, LAG), 自连接, 公共表表达式(WITH...AS)
阶段四:性能优化与维护 1. 为关键查询表建立索引 2. 将复杂查询物化为表,提升报表查询速度 CREATE INDEX, EXPLAIN分析执行计划, CREATE TABLE ... AS SELECT

3.2:开发阶段的设计

第一阶段是数据基础,让用户熟悉环境和数据;

第二阶段聚焦核心业务指标,这是产品经理最常问的问题;

第三阶段进入高级分析,涉及用户行为分析和商品关联挖掘,这是数据开发工程师的价值体现;

第四阶段则是性能和生产化考量,体现工程思维。

四:需求

以下四个阶段模拟了从数据基础到高阶分析、再到性能优化的完整工作流。

阶段 核心目标 具体开发需求与分析任务 关键技能与产出
阶段一:数据探查与质量核查 理解数据,发现潜在问题 任务1.1:数据概览 • 查询各表总记录数、时间范围 • 检查是否存在NULL任务1.2:业务逻辑校验 • 验证fact_order_details.subtotal是否等于quantity * price • 检查是否有用户购买了自己所在城市不配送的商品(模拟) COUNT, MIN/MAX, WHERE IS NULL, JOIN基础
阶段二:核心业务指标开发 产出可直接用于报表的聚合数据 任务2.1:创建用户宽表 • 聚合每个用户的累计订单数、总金额、最近购买日 • 标记近30天活跃用户 任务2.2:商品销售分析表 • 计算每个商品的销售总量、总销售额、平均售价 • 按商品类目聚合销售额和销量排名 GROUP BY聚合, CASE WHEN, CREATE VIEW, RANK()
阶段三:高级业务分析 挖掘数据深层价值,支持决策 任务3.1:用户复购分析 • 计算每月用户的复购率(当月购买≥2次) • 找出累计消费金额最高的"高价值用户" 任务3.2:商品关联分析 • 找出最常被一起购买的商品组合(如:智能手机+无线耳机) • 分析"电子产品"类用户的交叉购买率 窗口函数, 自连接, 公共表表达式(CTE)
阶段四:性能优化与脚本化 提升查询效率,实现自动化 任务4.1:查询性能优化 • 为fact_orders(order_date, user_id)创建复合索引 • 使用EXPLAIN分析慢查询的执行计划 任务4.2:数据快照与维护 • 创建"每日销售汇总"物化表,记录每天的核心指标 • 编写存储过程,一键清理测试订单数据 CREATE INDEX, EXPLAIN, CREATE TABLE AS, 存储过程基础

五:数据开发过程

sql 复制代码
---1:创建数据库
create database ecommerce_analysis;

use ecommerce_analysis


---2:用户维度表
create table dim_users(
   user_id int primary key,
   user_name varchar(50),
   city varchar(50),
   registration_date date
)

---3:商品维护表
create table dim_products(
  product_id int primary key,
  product_name varchar(100),
  category varchar(50) comment'电子产品',
  price decimal(10,2)
)

---4:订单事实表(核心交易记录)
create table fact_orders(
  order_id varchar(20) primary key,
  user_id int,
  order_date date,
  total_amount decimal(10,2),
  foreign key(user_id) references dim_users(user_id)
)

---5:订单明细表(记录订单中具体商品)
create table  fact_order_details(
 detail_id int auto_increment primary key,
 order_id varchar(20),
 product_id int,
 quantity int ,
 subtotal decimal(10,2),
 foreign key(order_id) references fact_orders(order_id),
 foreign key(product_id) references dim_products(product_id)
 )
 
 ---6: 插入数据
 insert into dim_users (user_id,user_name,city,registration_date) values
 (101,'张三','北京','2023-01-15'),
 (102,'李四','上海','2023-02-20'),
 (103,'王五','北京','2023-03-10'),
 (104,'赵六','广州','2023-01-05'),
 (105,'钱七','上海','2023-04-18')
 
 
 insert into dim_products(product_id,product_name,category,price) values
 (1,'智能手机','电子产品',2999.00),
 (2,'无线耳机','电子产品',399.00),
 (3,'男士T恤','服装',89.00),
 (4,'女士外套','服装',259.00),
 (5,'陶瓷碗套装','家居',120.00)
 

 insert into fact_orders(order_id,user_id,order_date,total_amount) values
('ORD202305001', 101, '2023-05-10', 3398.00),
('ORD202305002', 102, '2023-05-12', 399.00),
('ORD202305003', 103, '2023-05-15', 548.00),
('ORD202305004', 101, '2023-05-20', 259.00),
('ORD202305005', 104, '2023-05-22', 3278.00),
('ORD202306001', 102, '2023-06-05', 176.00),
('ORD202306002', 105, '2023-06-08', 120.00);


INSERT INTO fact_order_details (order_id, product_id, quantity, subtotal) VALUES
('ORD202305001', 1, 1, 2999.00),
('ORD202305001', 2, 1, 399.00),
('ORD202305002', 2, 1, 399.00),
('ORD202305003', 3, 2, 178.00),
('ORD202305003', 4, 1, 259.00),
('ORD202305003', 5, 1, 120.00),
('ORD202305004', 4, 1, 259.00),
('ORD202305005', 1, 1, 2999.00),
('ORD202305005', 5, 1, 120.00),
('ORD202306001', 3, 2, 178.00),
('ORD202306002', 5, 1, 120.00);

1: 阶段一 - 数据探查与质量核查

目的:理解数据,发现潜在问题

任务1:数据概览

1:查询各表总记录数,时间范围;

2:检查是否存在NULL值

任务2:业务逻辑校验

1:验证fact_order_details.subtotal 是否等于quantity * price

2: 检查是否有用户购买了自己所在城市不配送的商品(模拟)

关键技能与产出

count,max/min ,where is null,, join基础;

:像一个数据侦探,在开始分析前,先摸清数据的"家底"和质量。

任务 SQL语句与关键操作 执行结果(基于样本数据) 结果分析与洞察
1.1 数据概览 SELECT COUNT(*) AS cnt FROM fact_orders; SELECT MIN(order_date), MAX(order_date) FROM fact_orders; cnt: 7 日期范围:2023-05-10 到 2023-06-08 共7笔订单,时间跨度为约1个月。这是一个很小的数据集,适用于练习,但真实场景中应有更大数据量。
1.2 逻辑校验 SELECT d.* FROM fact_order_details d JOIN dim_products p ON d.product_id = p.product_id WHERE ABS(d.subtotal - (d.quantity * p.price)) > 0.01; 查询结果为空 没有发现明细金额与单价*数量不符的记录。通过基础数据一致性校验。这说明数据录入或ETL过程是准确的。

阶段一核心收获:你学会了在分析前首先要做"体检",确认数据量、时间范围和核心逻辑是否正确。这是所有可靠分析的地基。

sql 复制代码
---阶段一

---数据概览
---1:查询订单表总记录数,时间范围
select count(*) from dim_users 

select min(order_date),max(order_date) from fact_orders


---2:查询是否存在null值
select * from fact_orders where isnull(order_id)  


---业务逻辑校验
---3:验证fact_order_details.subtotal 是否等于quantity * price(验证订单明细的产品总价格 = 产品价格 x 产品购买数量)
---目的:	没有发现明细金额与单价*数量不符的记录。通过基础数据一致性校验。这说明数据录入或ETL过程是准确的
---结果为空,没误差,数据准确
select * from dim_products a join fact_order_details b on a.product_id = b.product_id
where abs(b.subtotal - (a.price * b.quantity)) > 0.01

2:阶段二

目标:将原始数据加工成可直接使用的业务指标,通常是创建"数据宽表"或"汇总视图"。

sql 复制代码
use ecommerce_analysis

---任务:创建用户宽表

---1:聚合每个用户的累计订单数,总金额(fact_order_details),商品总件数,最近购买日

select a.user_id,count(distinct b.order_id),count(c.detail_id),sum(c.subtotal) from dim_users a left join fact_orders b

on a.user_id = b.user_id

left join fact_order_details c

on b.order_id = c.order_id

group by a.user_id

---2:聚合每个用户的累计订单数,总金额(fact_order),商品总件数,最近购买日
--- 这里不能用sum(distinct b.total_amount) ,dinstinct去重,因为有不同的订单金额相同情况下,这样会少算;
select a.user_id,count(distinct b.order_id),count(c.detail_id),sum(distinct b.total_amount) from dim_users a left join fact_orders b

on a.user_id = b.user_id

left join fact_order_details c

on b.order_id = c.order_id

group by a.user_id

---正确方法

select 
   a.user_id ,
   -- 从订单表直接获取订单数和金额
   coalesce(b.total_orders,0) as total_orders,
   -- 从订单明细获取商品件
   coalesce (b.total_amount,0) as total_amount,
   coalesce (c.total_items,0) as total_items
from dim_users a
left join(
   --- 每个用户的订单聚合
  select 
         user_id , --- 关键,这里要order_id
         order_id,
         count(*) as total_orders ,
         sum(total_amount) as total_amount,
         max(order_date)
         from fact_orders fo 
         group by user_id 
) b on a.user_id = b.user_id

left join(
  -- 每个用户的商品件数聚和
  select order_id , count(*) as total_items from fact_order_details c group by order_id 

)c on c.order_id = b.order_id



注意:c.order_id = b.order_id 报错;不存在b.order_id ,因为b子连接查询中要添加order_id

2:计算每个商品的销售总量,总销售额,平均售价

sql 复制代码
select 
   dp.product_id,
   dp.product_name,
   sum(fod.quantity) as total_quantity,
   sum(fod.subtotal) as total_price,
   avg(fod.subtotal / fod.quantity) as avg_price
from dim_products dp 
left join fact_order_details fod 
ON  dp.product_id = fod .product_id 
group by dp.product_id 
order by product_id asc

以上SQL存在风险:

  1. avg_price 的计算隐患 :你使用 fod.subtotal / fod.quantity 作为 AVG 的参数。这里存在两个风险:

    • 除零错误 :如果某条明细的 quantity 为 0,数据库会抛出除零错误。

    • NULL值干扰AVG 函数会忽略 NULL。如果 subtotalquantityNULL,会导致除法结果为 NULL,从而被 AVG 忽略,这可能扭曲最终的平均值。

  2. 无销售商品的结果 :由于使用 LEFT JOIN,没有销售记录的商品,其 SUM 结果将是 NULL 而不是 0。在报表中,NULL 通常不如 0 直观。

优化SQL

sql 复制代码
SELECT 
    dp.product_id,
    dp.product_name,
    -- 使用COALESCE将NULL转换为0,使报表更清晰
    COALESCE(SUM(fod.quantity), 0) AS total_quantity_sold,
    COALESCE(SUM(fod.subtotal), 0) AS total_sales_amount,
    -- 安全地计算平均售价:总销售额 / 总销售数量
    -- 使用NULLIF避免除零错误,当总数量为0时,平均价显示为NULL(可再用COALESCE赋默认值)
    COALESCE(
        SUM(fod.subtotal) / NULLIF(SUM(fod.quantity), 0), 
        0
    ) AS avg_selling_price
FROM 
    dim_products dp 
LEFT JOIN 
    fact_order_details fod ON dp.product_id = fod.product_id 
GROUP BY 
    dp.product_id,
    dp.product_name -- 通常需要将SELECT中所有非聚合字段都加入GROUP BY
ORDER BY 
    dp.product_id ASC; -- 显式指明表别名是更好的实践

3:按商品类目聚合销售额和类目的销售排名

sql 复制代码
select 
    dp.category,
    coalesce (sum(fod.subtotal),0) as cate_total_price,
    row_number() OVER(order by coalesce(sum(fod.subtotal),0) desc) as cate_rank
    from dim_products dp 
left join fact_order_details fod 
on dp.product_id = fod.product_id 
group by dp.category 
order by cate_total_price desc
相关推荐
Insist7532 小时前
KingbaseES 集群运维案例之 --- 集群架构拆分为单实例操作
网络·数据库·oracle
lkbhua莱克瓦242 小时前
进阶-存储对象1-视图
java·数据库·sql·mysql·视图
IvorySQL2 小时前
用 PostgreSQL 实践 Palantir 本体论
数据库·postgresql·开源
yangminlei2 小时前
Spring Boot 自动配置原理与自定义 Starter 开发实战
java·数据库·spring boot
萧曵 丶2 小时前
Redis 由浅到深面试题(分层次版)
数据库·redis·缓存
-XWB-2 小时前
【Oracle】Oracle诊断系列(1/6):健康体检指南——快速掌握数据库状态
数据库·oracle
杨了个杨89822 小时前
Redis常用命令
数据库·redis·缓存
-XWB-3 小时前
【MySQL】XtraBackup 全量备份还原操作指南(MySQL 5.7 / 8.0 通用)
数据库·mysql·adb
oMcLin3 小时前
如何在 CentOS 7 上通过配置 MySQL Cluster 部署高可用的跨境电商数据库,确保数据同步与高并发处理
mysql·centos