
文章目录
-
- [📦 mysql - 安装包下载(目的是:动手实操)](#📦 mysql - 安装包下载(目的是:动手实操))
- [📦 学习阶段一:win11 下 数据库安装(win系统通用 - 目的是可以给其他人 安装数据库 - 有机会装一下)](#📦 学习阶段一:win11 下 数据库安装(win系统通用 - 目的是可以给其他人 安装数据库 - 有机会装一下))
- [📦 学习阶段二:打开 mysql 命令行客户端(目的是熟悉 常用sql命令)](#📦 学习阶段二:打开 mysql 命令行客户端(目的是熟悉 常用sql命令))
- [📦 学习阶段三:使用 Navicat 进行数据库管理访问(目的是:通过图形界面 快速操作数据库、你可以认为它是数据库的图形化操作系统【管理工具】)](#📦 学习阶段三:使用 Navicat 进行数据库管理访问(目的是:通过图形界面 快速操作数据库、你可以认为它是数据库的图形化操作系统【管理工具】))
-
-
- 建立数据库连接
- [数据库 表管理(替代 命令窗口)](#数据库 表管理(替代 命令窗口))
- [创建 zx_book 数据库和3个表](#创建 zx_book 数据库和3个表)
-
- [📦 学习阶段四:pycharm(编辑器) 写 python 程序 (基于conda环境、数据库增删改查) (目的是:基于python学会一点数据报表、数据分析)](#📦 学习阶段四:pycharm(编辑器) 写 python 程序 (基于conda环境、数据库增删改查) (目的是:基于python学会一点数据报表、数据分析))
-
- [📦 项目结构(请创建以下文件)](#📦 项目结构(请创建以下文件))
- [1️⃣ `requirements.txt`(依赖包)](#1️⃣
requirements.txt(依赖包)) - [2️⃣ `config.py`(数据库配置)](#2️⃣
config.py(数据库配置)) - [3️⃣ `generate_mock_data.py`(生成2025年全年模拟数据)](#3️⃣
generate_mock_data.py(生成2025年全年模拟数据)) - [4️⃣ `queries.py`(所有数据查询逻辑)](#4️⃣
queries.py(所有数据查询逻辑)) - [5️⃣ `app.py`(Flask 主程序)](#5️⃣
app.py(Flask 主程序)) - [6️⃣ `templates/index.html`(前端页面)](#6️⃣
templates/index.html(前端页面)) - [▶️ 使用步骤(小白友好)](#▶️ 使用步骤(小白友好))
- [📦 学习阶段五:各种丰富的多表联合 - 增删改查](#📦 学习阶段五:各种丰富的多表联合 - 增删改查)
- [❤️ 一起学习用 AI 创造可能](#❤️ 一起学习用 AI 创造可能)
📦 mysql - 安装包下载(目的是:动手实操)
- https://dev.mysql.com/downloads/installer/
- 不同的操作系统需要下载不同的安装包

📦 学习阶段一:win11 下 数据库安装(win系统通用 - 目的是可以给其他人 安装数据库 - 有机会装一下)
最小单元安装 - 只安装 Server 端即可

剩下的基本 一路默认就好了









📦 学习阶段二:打开 mysql 命令行客户端(目的是熟悉 常用sql命令)
- mysql 命令行客户端 是数据库本身提供的命令行交互工具(类似于 win系统的 cmd 命令窗口、Linux 的 shell 交互窗口)


创建数据库、查询数据库
bash
mysql> SHOW DATABASES;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
| performance_schema |
| school |
| sys |
+--------------------+
5 rows in set (0.00 sec)
mysql> CREATE DATABASE mydatabase
-> CHARACTER SET utf8mb4
-> COLLATE utf8mb4_general_ci;
Query OK, 1 row affected (0.00 sec)
mysql> SHOW DATABASES;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mydatabase |
| mysql |
| performance_schema |
| school |
| sys |
+--------------------+
6 rows in set (0.00 sec)
mysql> use mydatabase;
Database changed
mysql> SELECT * FROM person;
+----------------+------+----+
| name | age | id |
+----------------+------+----+
| Yokoyama Ayato | 315 | 1 |
| Paul Taylor | 940 | 2 |
| Yip Kwok Kuen | 871 | 3 |
| Luo Yunxi | 845 | 4 |
| Edith Perry | 382 | 5 |
| Carl Owens | 155 | 6 |
| Duan Yuning | 963 | 7 |
| Yu Jialun | 931 | 8 |
| 11 | 21 | 10 |
+----------------+------+----+
9 rows in set (0.00 sec)
📦 学习阶段三:使用 Navicat 进行数据库管理访问(目的是:通过图形界面 快速操作数据库、你可以认为它是数据库的图形化操作系统【管理工具】)
- https://www.navicat.com.cn/products
- 可以去下载一些 Navicat 破解 版本
建立数据库连接

数据库 表管理(替代 命令窗口)

创建 zx_book 数据库和3个表
- 借助 Navicat 的命令列界面即可执行 sql 命令


📦 学习阶段四:pycharm(编辑器) 写 python 程序 (基于conda环境、数据库增删改查) (目的是:基于python学会一点数据报表、数据分析)
这里呢,直接去用某个大模型产品、提出你的需求,告诉他 我的数据库用户信息即可
- 示例要求如下
bash
我在做数据分析运营,请帮我设计一套完整的 python数据分析 前后端程序;以中信书店为目标场景,我希望在前端网页建立书本销量、用户 等周期性数据看板;首先请你写一个脚本帮我模拟一个年度的每天用户数据、销量数据;写入到我的数据库中;其次请你丰富前端的数据看板,我觉得应该有一些基础的数据条件查询;增加"区域销售热力图"(需用户地址字段)
添加"复购率"、"客单价"指标
支持时间范围筛选(前端传参 → 后端动态查询)
我的数据库是mysql;数据库连接信息为:
db = mysql.connector.connect(
host="localhost",
user="root",
password="123456",
database="zx_book"
)
通过 某大模型 (通以千问)的返回生成如下
没问题!作为小白程序员,你只需要按步骤操作即可。下面我为你打包一个 完整可运行的项目,包含:
- ✅ 模拟数据生成脚本
- ✅ Flask 后端(带时间筛选、复购率、客单价、省份销量)
- ✅ 前端看板(含日期选择器 + 6个图表 + KPI卡片)
- ✅ 所有依赖和说明
📦 项目结构(请创建以下文件)
bash
zx_book_dashboard/
├── config.py
├── generate_mock_data.py
├── queries.py
├── app.py
├── requirements.txt
└── templates/
└── index.html
1️⃣ requirements.txt(依赖包)
txt
flask
mysql-connector-python
pandas
安装命令:
pip install -r requirements.txt
2️⃣ config.py(数据库配置)
python
# config.py
DB_CONFIG = {
'host': 'localhost',
'user': 'root',
'password': '123456',
'database': 'zx_book'
}
3️⃣ generate_mock_data.py(生成2025年全年模拟数据)
python
# generate_mock_data.py
import mysql.connector
import random
from datetime import datetime, timedelta
from config import DB_CONFIG
CITY_PROVINCE = {
'北京': '北京市', '上海': '上海市', '广州': '广东省', '深圳': '广东省',
'杭州': '浙江省', '成都': '四川省', '武汉': '湖北省', '西安': '陕西省',
'南京': '江苏省', '天津': '天津市', '重庆': '重庆市', '苏州': '江苏省',
'郑州': '河南省', '长沙': '湖南省', '青岛': '山东省', '大连': '辽宁省'
}
BOOK_TITLES = [
'认知觉醒', '原则', '人类简史', '三体', '活着', '穷查理宝典',
'Python数据分析', '时间简史', '小王子', '围城', '百年孤独', '追风筝的人'
]
BOOK_CATEGORIES = ['文学', '社科', '经管', '科技', '生活', '少儿', '艺术', '历史']
def create_tables(conn):
cursor = conn.cursor()
cursor.execute("DROP TABLE IF EXISTS orders, users, books")
cursor.execute("""
CREATE TABLE books (
book_id INT PRIMARY KEY AUTO_INCREMENT,
title VARCHAR(100),
category VARCHAR(50),
price DECIMAL(10,2)
)
""")
cursor.execute("""
CREATE TABLE users (
user_id INT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(50),
reg_date DATE,
city VARCHAR(50),
province VARCHAR(50)
)
""")
cursor.execute("""
CREATE TABLE orders (
order_id INT PRIMARY KEY AUTO_INCREMENT,
user_id INT,
book_id INT,
order_date DATETIME,
quantity INT,
total_price DECIMAL(10,2),
FOREIGN KEY (user_id) REFERENCES users(user_id),
FOREIGN KEY (book_id) REFERENCES books(book_id)
)
""")
conn.commit()
def insert_mock_data():
conn = mysql.connector.connect(**DB_CONFIG)
create_tables(conn)
cursor = conn.cursor()
# 插入书籍
books = []
for i, title in enumerate(BOOK_TITLES):
cat = random.choice(BOOK_CATEGORIES)
price = round(random.uniform(25, 89), 2)
books.append((i+1, title, cat, price))
cursor.executemany("INSERT INTO books (book_id, title, category, price) VALUES (%s, %s, %s, %s)", books)
# 插入用户
users = []
user_id = 1
start_date = datetime(2025, 1, 1)
end_date = datetime(2025, 12, 31)
current = start_date
while current <= end_date:
daily_new = random.randint(10, 30)
for _ in range(daily_new):
city = random.choice(list(CITY_PROVINCE.keys()))
province = CITY_PROVINCE[city]
users.append((user_id, f"user_{user_id}", current.date(), city, province))
user_id += 1
current += timedelta(days=1)
cursor.executemany("INSERT INTO users (user_id, username, reg_date, city, province) VALUES (%s, %s, %s, %s, %s)", users)
# 插入订单
orders = []
order_id = 1
current = start_date
while current <= end_date:
daily_orders = random.randint(50, 200)
for _ in range(daily_orders):
uid = random.randint(1, len(users))
bid = random.randint(1, len(books))
qty = random.randint(1, 5)
price = books[bid-1][3]
total = round(qty * price, 2)
order_time = current + timedelta(
hours=random.randint(0, 23),
minutes=random.randint(0, 59),
seconds=random.randint(0, 59)
)
orders.append((order_id, uid, bid, order_time, qty, total))
order_id += 1
current += timedelta(days=1)
cursor.executemany(
"INSERT INTO orders (order_id, user_id, book_id, order_date, quantity, total_price) VALUES (%s, %s, %s, %s, %s, %s)",
orders
)
conn.commit()
cursor.close()
conn.close()
print(f"✅ 模拟数据生成完成!共 {len(users)} 用户,{len(orders)} 订单。")
if __name__ == "__main__":
insert_mock_data()
4️⃣ queries.py(所有数据查询逻辑)
python
# queries.py
import mysql.connector
import pandas as pd
from config import DB_CONFIG
def get_db_connection():
return mysql.connector.connect(**DB_CONFIG)
def sales_by_day_range(start_date, end_date):
conn = get_db_connection()
query = """
SELECT DATE(order_date) AS date, SUM(quantity) AS total_qty
FROM orders
WHERE order_date BETWEEN %s AND %s
GROUP BY DATE(order_date)
ORDER BY date;
"""
df = pd.read_sql(query, conn, params=[start_date, end_date])
conn.close()
return df.to_dict(orient='records')
def top_books_by_sales_range(start_date, end_date, limit=10):
conn = get_db_connection()
query = """
SELECT b.title, SUM(o.quantity) AS total_sold
FROM orders o
JOIN books b ON o.book_id = b.book_id
WHERE o.order_date BETWEEN %s AND %s
GROUP BY b.book_id, b.title
ORDER BY total_sold DESC
LIMIT %s;
"""
df = pd.read_sql(query, conn, params=[start_date, end_date, limit])
conn.close()
return df.to_dict(orient='records')
def new_users_by_week_range(start_date, end_date):
conn = get_db_connection()
query = """
SELECT
YEARWEEK(reg_date, 1) AS week,
COUNT(*) AS new_users
FROM users
WHERE reg_date BETWEEN %s AND %s
GROUP BY week
ORDER BY week;
"""
df = pd.read_sql(query, conn, params=[start_date, end_date])
conn.close()
return df.to_dict(orient='records')
def sales_by_category_range(start_date, end_date):
conn = get_db_connection()
query = """
SELECT b.category, SUM(o.quantity) AS total_qty
FROM orders o
JOIN books b ON o.book_id = b.book_id
WHERE o.order_date BETWEEN %s AND %s
GROUP BY b.category
ORDER BY total_qty DESC;
"""
df = pd.read_sql(query, conn, params=[start_date, end_date])
conn.close()
return df.to_dict(orient='records')
def sales_by_province_range(start_date, end_date):
conn = get_db_connection()
query = """
SELECT u.province, SUM(o.quantity) AS total_qty
FROM orders o
JOIN users u ON o.user_id = u.user_id
WHERE o.order_date BETWEEN %s AND %s
GROUP BY u.province
ORDER BY total_qty DESC;
"""
df = pd.read_sql(query, conn, params=[start_date, end_date])
conn.close()
return df.to_dict(orient='records')
def get_kpi_range(start_date, end_date):
conn = get_db_connection()
query = """
SELECT
COUNT(DISTINCT user_id) AS total_users,
COUNT(DISTINCT CASE WHEN user_order_count > 1 THEN user_id END) AS repeat_users,
ROUND(AVG(total_per_user), 2) AS avg_order_value
FROM (
SELECT
user_id,
COUNT(*) AS user_order_count,
SUM(total_price) AS total_per_user
FROM orders
WHERE order_date BETWEEN %s AND %s
GROUP BY user_id
) AS user_stats;
"""
df = pd.read_sql(query, conn, params=[start_date, end_date])
conn.close()
if df.empty or df.iloc[0]['total_users'] is None:
return {"repeat_rate": 0, "avg_order_value": 0}
total = df.iloc[0]['total_users'] or 1
repeat = df.iloc[0]['repeat_users'] or 0
aov = df.iloc[0]['avg_order_value'] or 0
repeat_rate = round(repeat / total * 100, 2)
return {"repeat_rate": repeat_rate, "avg_order_value": float(aov)}
5️⃣ app.py(Flask 主程序)
python
# app.py
from flask import Flask, render_template, jsonify, request
from queries import (
sales_by_day_range,
top_books_by_sales_range,
new_users_by_week_range,
sales_by_category_range,
sales_by_province_range,
get_kpi_range
)
app = Flask(__name__)
@app.route('/')
def dashboard():
return render_template('index.html')
@app.route('/api/sales-by-day')
def api_sales_by_day():
start = request.args.get('start', '2025-01-01')
end = request.args.get('end', '2025-12-31')
return jsonify(sales_by_day_range(start, end))
@app.route('/api/top-books')
def api_top_books():
start = request.args.get('start', '2025-01-01')
end = request.args.get('end', '2025-12-31')
return jsonify(top_books_by_sales_range(start, end))
@app.route('/api/new-users')
def api_new_users():
start = request.args.get('start', '2025-01-01')
end = request.args.get('end', '2025-12-31')
return jsonify(new_users_by_week_range(start, end))
@app.route('/api/sales-by-category')
def api_sales_by_category():
start = request.args.get('start', '2025-01-01')
end = request.args.get('end', '2025-12-31')
return jsonify(sales_by_category_range(start, end))
@app.route('/api/sales-by-province')
def api_sales_by_province():
start = request.args.get('start', '2025-01-01')
end = request.args.get('end', '2025-12-31')
return jsonify(sales_by_province_range(start, end))
@app.route('/api/kpi')
def api_kpi():
start = request.args.get('start', '2025-01-01')
end = request.args.get('end', '2025-12-31')
return jsonify(get_kpi_range(start, end))
if __name__ == '__main__':
app.run(debug=True, host='0.0.0.0', port=5000)
6️⃣ templates/index.html(前端页面)
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>中信书店数据看板</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<style>
.chart-container { height: 300px; margin-bottom: 20px; }
.kpi-card { text-align: center; padding: 1.5rem; font-size: 1.5rem; }
</style>
</head>
<body>
<div class="container mt-4">
<h1 class="text-center mb-4">中信书店运营数据看板</h1>
<!-- 时间筛选 -->
<div class="row mb-4">
<div class="col-md-5">
<label>开始日期</label>
<input type="date" id="startDate" class="form-control" value="2025-01-01">
</div>
<div class="col-md-5">
<label>结束日期</label>
<input type="date" id="endDate" class="form-control" value="2025-12-31">
</div>
<div class="col-md-2 d-flex align-items-end">
<button class="btn btn-primary w-100" onclick="refreshData()">刷新</button>
</div>
</div>
<!-- KPI 卡片 -->
<div class="row mb-4">
<div class="col-md-6">
<div class="card bg-light">
<div class="kpi-card">
<div>复购率</div>
<div id="repeatRate">--%</div>
</div>
</div>
</div>
<div class="col-md-6">
<div class="card bg-light">
<div class="kpi-card">
<div>客单价</div>
<div id="avgOrderValue">¥--</div>
</div>
</div>
</div>
</div>
<!-- 图表区域 -->
<div class="row">
<div class="col-md-6">
<div class="card"><div class="card-header">📅 每日销量(册)</div><div class="card-body"><canvas id="salesChart"></canvas></div></div>
</div>
<div class="col-md-6">
<div class="card"><div class="card-header">📚 销量Top10书籍</div><div class="card-body"><canvas id="topBooksChart"></canvas></div></div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="card"><div class="card-header">👥 每周新增用户</div><div class="card-body"><canvas id="newUsersChart"></canvas></div></div>
</div>
<div class="col-md-6">
<div class="card"><div class="card-header">📊 品类销量分布</div><div class="card-body"><canvas id="categoryChart"></canvas></div></div>
</div>
</div>
<div class="row">
<div class="col-12">
<div class="card"><div class="card-header">📍 各省份销量分布</div><div class="card-body"><canvas id="provinceChart"></canvas></div></div>
</div>
</div>
</div>
<script>
let currentStart = '2025-01-01';
let currentEnd = '2025-12-31';
window.charts = {};
function buildUrl(endpoint) {
return `${endpoint}?start=${currentStart}&end=${currentEnd}`;
}
async function loadKPI() {
const res = await fetch(buildUrl('/api/kpi'));
const data = await res.json();
document.getElementById('repeatRate').textContent = data.repeat_rate + '%';
document.getElementById('avgOrderValue').textContent = '¥' + data.avg_order_value;
}
function refreshData() {
currentStart = document.getElementById('startDate').value;
currentEnd = document.getElementById('endDate').value;
loadAllCharts();
}
async function loadAllCharts() {
loadKPI();
const configs = {
'salesChart': {
type: 'line',
data: {
labels: [],
datasets: [{ label: '销量(册)', data: [], borderColor: 'rgb(75,192,192)', tension: 0.1 }]
}
},
'topBooksChart': {
type: 'bar',
data: {
labels: [],
datasets: [{ label: '总销量', data: [], backgroundColor: 'rgba(54,162,235,0.6)' }]
},
options: { indexAxis: 'y' }
},
'newUsersChart': {
type: 'line',
data: {
labels: [],
datasets: [{ label: '新增用户', data: [], borderColor: 'rgb(255,99,132)' }]
}
},
'categoryChart': {
type: 'pie',
data: {
labels: [],
datasets: [{ data: [], backgroundColor: ['#FF6384','#36A2EB','#FFCE56','#4BC0C0','#9966FF'] }]
}
},
'provinceChart': {
type: 'bar',
data: {
labels: [],
datasets: [{ label: '销量(册)', data: [], backgroundColor: 'rgba(153,102,255,0.6)' }]
}
}
};
const endpoints = {
'salesChart': '/api/sales-by-day',
'topBooksChart': '/api/top-books',
'newUsersChart': '/api/new-users',
'categoryChart': '/api/sales-by-category',
'provinceChart': '/api/sales-by-province'
};
for (const [chartId, config] of Object.entries(configs)) {
const res = await fetch(buildUrl(endpoints[chartId]));
const data = await res.json();
if (chartId === 'salesChart') {
config.data.labels = data.map(d => d.date);
config.data.datasets[0].data = data.map(d => d.total_qty);
} else if (chartId === 'topBooksChart') {
config.data.labels = data.map(d => d.title.length > 12 ? d.title.slice(0,10)+'...' : d.title);
config.data.datasets[0].data = data.map(d => d.total_sold);
} else if (chartId === 'newUsersChart') {
config.data.labels = data.map(d => 'W' + (d.week % 100));
config.data.datasets[0].data = data.map(d => d.new_users);
} else if (chartId === 'categoryChart') {
config.data.labels = data.map(d => d.category);
config.data.datasets[0].data = data.map(d => d.total_qty);
} else if (chartId === 'provinceChart') {
config.data.labels = data.map(d => d.province);
config.data.datasets[0].data = data.map(d => d.total_qty);
}
const ctx = document.getElementById(chartId).getContext('2d');
if (window.charts[chartId]) window.charts[chartId].destroy();
config.options = { ...config.options, responsive: true, maintainAspectRatio: false };
window.charts[chartId] = new Chart(ctx, config);
}
}
loadAllCharts();
</script>
</body>
</html>
▶️ 使用步骤(小白友好)
-
确保 MySQL 已安装,并创建数据库:
sqlCREATE DATABASE zx_book CHARACTER SET utf8mb4; -
安装 Python 依赖:
bashpip install -r requirements.txt -
生成模拟数据(只需运行一次):
bashpython generate_mock_data.py
运行成功输出如下
bash
python generate_mock_data.py
✅ 模拟数据生成完成!共 7312 用户,45740 订单。
- 启动 Web 服务:
bash
python app.py
- 打开浏览器,访问:
bash
http://localhost:5000
完整的前后端数据库交互案例就有了,这是很基础的一个版本,更加丰富的功能,可以让大模型帮助我们逐步完善
- 浏览器前端网页效果如下:

📦 学习阶段五:各种丰富的多表联合 - 增删改查
进阶成为高级 - 资深 数据分析砖家- 跟着 XX 大模型学习即可
❤️ 一起学习用 AI 创造可能
- ❤️ 如果文章对你有些许帮助、蟹蟹各位读者大大点赞、评论鼓励博主的每一分认真创作
