電商訂單綜合小項目
1. 項目簡介
1.1業務背景介紹
- 本案例圍繞某個互聯網小型電商的訂單業務來開發。某電商公司,每天都有一些的用戶會在線上採購商品,該電商公司想通過數據分析,查看每一天的電商經營情況。例如:電商公司的運營部門想要清楚的看到每天的訂單筆數、訂單的下單總額、不同支付類型對應的訂單筆數和總額等等。
- 為了實現公司的數據分析業務,我們需要開發一套數據分析的系統以實現電商公司的需求
1.2電商的購物流程
- 這裡就假設為京東購物舉例
從上圖可以看到,每一個用戶購買商品,都會瀏覽商品、提交訂單。所以商品、訂單是電商中非常重要的。業務將主要圍繞商品、訂單展開。
1.3電商系統的簡單介紹
- 用戶打開瀏覽器,訪問電商頁面
- 用戶按下回車後,瀏覽器發出請求,請求電商網站的Web伺服器
- Web伺服器從資料庫取出數據,返回
- 前端頁面展示數據
結論
數據是存放在資料庫中,我們開展數據分析只需要能夠獲取到資料庫中的數據即可
1.4電商的相關業務術語介紹
1. 我們去淘寶、京東上買的東西,就是一件件的商品。商品包含這些內容:
1. 商品的標題
2. 商品的所屬分類
3. 商品的價格
4. 商品的顏色
5. 商品的版本
6. 商品的介紹
1.4.2商品分類
- 絕大多數的電商都有商品商品。每個商品一定是會屬於某個類別。例如:電冰箱屬於 家用電器 > 大家電 分類。不同的商品可能對應的分類是不一樣的。
1.4.3訂單
- 用戶購買商品是通過提交訂單來完成的。用戶每一次購物都會有訂單,訂單中包含了訂單號、收貨人、訂單狀態、支付方式、商品評價等。
1.4.4 訂單詳情
- 用戶可能在一次購買中,買了多個商品。訂單詳情指的是訂單走過來包含的具體資訊。例如:訂單中包含的商品資訊、商品金額、商品數量等。
1.4.5 用戶
- 想要購買商品,需要先在電商網站上註冊用戶。用戶包含了很多資訊,例如:用戶名、密碼、性別、生日、以及手機、密碼等資訊。
1.4.6 區域
每個訂單都有區域的概念,例如:我們可以選擇配送到哪兒,是配置到北京市昌平區百善鎮還是其他地方。
1.5 技術方案介紹
本項目基於MySQL資料庫,使用Kettle、Apache Superset實現數據可視化。案例使用MySQL作為數據分析的存儲以及查詢引擎、以Kettle作為數據處理腳本執行工具、以及Apache Superset實現數據可視化展示。
案例的最終效果如下:
2.項目架構說明
2.1 系統架構
2.2數據流程圖
- 通過Kettle將MySQL業務系統資料庫中,將數據抽取出來,然後裝載到MySQL數據倉庫中。
- 編寫SQL腳本,對MySQL數據倉庫中的數據進行數據分析(分組、聚合等),並將分析後的結果保存。
- 使用 Superset 將保存下來的分析結果以圖形的方式展示出來。
3.搭建項目環境
這裡是寫部落格步驟又不可以少所以
第一步
- 搭建虛擬機
- 安裝jdk
- 安裝MySQL8.0
- 安裝superset
有問題參考我部落格的安裝文檔
第二步
- 用SQLyog執行連接虛擬機的MySQL
第三部
執行下面SQL腳本
//files.cnblogs.com/files/it-wp/BI%E6%A1%88%E4%BE%8B%E6%95%B0%E6%8D%AE%E5%BA%93%E8%84%9A%E6%9C%AC.7z
創建兩個偽數倉
#ods層數據倉庫 create database if not exists ods_it_shop; #APP層數據倉庫 create database if not exists it_shop_bi;
4. 項目開發
4.1 表結構概覽
表名 |
說明 |
it_areas |
行政區域表,例如:北京市、昌平區等。 |
it_goods
|
商品表,保存了商品的基本資訊。例如:商品的唯一標識、商品的名稱、店鋪ID、商品的分類等。 |
it_goods_cats
|
商品分類表,每一個商品都有自己的分類。例如:海爾的某個冰箱屬於:家用電器 > 大家電 > 冰箱 這樣的一個分類。 |
it_orders
|
訂單表,用戶提交的訂單將保存在該表中。表中包含了:下訂單的用戶、訂單的狀態、訂單的支付金額、訂單所屬的區域、用戶所屬的地址等。 |
it_order_goods
|
訂單明細表,訂單明細表包含了訂單中的包含的商品資訊。用戶可以同時買多個商品,然後提交一個訂單。例如:提交的訂單中包含一個手機、和一個電冰箱。訂單明細中包含了用戶買的商品數據和訂單資訊。例如:該訂單明細對應的訂單是什麼、買了幾個這樣的商品、商品的ID是什麼等。 |
it_users
|
用戶資訊表,包含了用戶的ID、用戶名、密碼等資訊。 |
4.2 表欄位解釋
it_areas/行政區域表
列名 |
類型 |
說明 |
areaId |
int(11) |
區域ID |
parentId |
int(11) |
父ID |
areaName |
varchar(100) |
地區名稱 |
areaKey |
char(10) |
地區首字母 |
areaType |
tinyint(4) |
級別標誌1:省,2:市,3:縣區 |
createTime |
varchar(25) |
創建時間 |
it_goods/商品表
列名 |
類型 |
說明 |
goodsId |
bigint(11) |
商品id |
goodsSn |
varchar(20) |
商品編號 |
goodsName |
varchar(200) |
商品名稱 |
goodsImg |
varchar(150) |
商品圖片 |
shopId |
bigint(11) |
門店ID |
marketPrice |
decimal(11,2) |
市場價 |
shopPrice |
decimal(11,2) |
門店價 |
isHot |
tinyint(4) |
是否熱銷產品 |
goodsCatId |
int(11) |
goodsCatId |
saleNum |
int(11) |
總銷售量 |
createTime |
varchar(25) |
創建時間 |
it_goods_cats/商品分類
列名 |
類型 |
說明 |
catId |
int(11) |
品類ID |
parentId |
int(11) |
父ID |
catName |
varchar(20) |
分類名稱 |
dataFlag |
tinyint(4) |
刪除標誌 |
createTime |
varchar(25) |
建立時間 |
cat_level |
tinyint(4) |
分類級別,共3級 |
it_orders/訂單表
列名 |
類型 |
說明 |
orderId |
bigint(11) |
訂單id |
orderNo |
varchar(20) |
訂單編號 |
shopId |
bigint(11) |
門店id |
userId |
bigint(11) |
用戶id |
orderStatus |
tinyint(4) |
訂單狀態 -3:用戶拒收 -2:未付款的訂單 -1:用戶取消 0: |
goodsMoney |
decimal(11,2) |
商品金額 |
deliverMoney |
decimal(11,2) |
運費 |
totalMoney |
decimal(11,2) |
訂單金額(包括運費) |
realTotalMoney |
decimal(11,2) |
實際訂單金額(折扣後金額) |
payType |
tinyint(4) |
支付方式,0:未知;1:支付寶,2:微信;3、現金;4、其他 |
isPay |
tinyint(4) |
是否支付 |
userName |
varchar(20) |
收件人姓名 |
userAddress |
varchar(255) |
收件人地址 |
userPhone |
char(20) |
收件人電話 |
createTime |
varchar(25) |
下單時間 |
noticeDeliver |
tinyint(3) unsigned |
提醒發貨 0:未提醒 1:已提醒 |
payTime |
varchar(25) |
支付時間 |
totalPayFee |
int(11) |
總支付金額 |
it_order_goods/訂單明細表
列名 |
類型 |
說明 |
ogId |
bigint(11) |
訂單明細ID |
orderId |
bigint(11) |
訂單ID |
goodsId |
bigint(11) |
商品ID |
goodsNum |
bigint(11) |
商品數量 |
goodsPrice |
decimal(13,0) |
商品價格 |
payPrice |
decimal(13,0) |
實際支付價格 |
goodsName |
varchar(600) |
商品名稱 |
goodsImg |
varchar(450) |
商品圖片 |
createtime |
varchar(75) |
創建時間 |
it_users/用戶表
列名 |
類型 |
說明 |
userId |
int(11) |
用戶ID |
loginName |
varchar(20) |
登錄名 |
loginSecret |
int(11) |
登錄秘鑰 |
loginPwd |
varchar(50) |
登錄密碼 |
userSex |
tinyint(4) |
用戶性別 |
userName |
varchar(100) |
用戶名 |
trueName |
varchar(100) |
用戶真實姓名 |
brithday |
date |
生日 |
userPhoto |
varchar(200) |
用戶照片 |
userQQ |
varchar(20) |
用戶QQ |
userPhone |
char(11) |
用戶電話 |
userScore |
int(11) |
用戶積分 |
userTotalScore |
int(11) |
用戶總積分 |
userFrom |
tinyint(4) |
用戶來源 |
userMoney |
decimal(11,2) |
用戶現金 |
lockMoney |
decimal(11,2) |
賬戶現金 |
createTime |
datetime |
創建時間 |
payPwd |
varchar(100) |
支付密碼 |
rechargeMoney |
decimal(11,2) |
充值金額 |
4.3.1 數據抽取業務分析
我們已經大概熟悉了上面的6張表,這6張表有時候並不是將所有數據一次性原封不動地同步到數據倉庫中,而是有一些處理細節。考慮以下幾個可能出現的業務場景:
1. 每一天都需要進行訂單的分析,例如:2021年2月2日一共有多少筆訂單、訂單的總額是多少。
2. 每一天都需要進行用戶的分析,例如:2021年2月2日一共註冊有多少個用戶。
3. 商品分類、區域的變化率很少,因為分類、區域幾乎都是常年不變的。
4. 商品的數據相對變化頻率較高,因為可能每天都會有商品資訊的更新。
表名 |
說明 |
裝載表 |
抽取方式 |
抽取周期 |
it_areas |
行政區域表 |
ods_it_areas |
增量同步抽取 |
每年 |
it_goods |
商品表 |
ods_it_goods |
增量同步抽取 |
每天 |
it_goods_cats |
商品分類表 |
ods_it_goods_cats |
全量同步抽取 |
每周 |
it_orders |
訂單表 |
ods_it_orders |
增量同步抽取 |
每天 |
it_order_goods |
訂單明細表 |
ods_it_order_goods |
增量同步抽取 |
每天 |
it_users |
用戶資訊表 |
ods_it_users |
增量同步抽取 |
每天 |
- 全量同步抽取:將所有數據同步抽取到數據倉庫
- 增量同步抽取:只抽取新增的數據到數據倉庫
4.3.2 關於 ods 的意義
- ODS(英語:Operational 業務/ Data 數據/ Store 存儲)是一種數據架構或資料庫設計的概念,出現原因是來自於當需要集成來自多個系統的數據,結果又要給一或多個系統使用時。
- 數據倉庫的ods表是將業務系統資料庫表原樣抽取進來,結構幾乎是一樣的,只不過加了一個抽取數據的日期欄位。
4.3.3 每周數據抽取作業開發
4.3.3.1 開發行政區域數據抽取
資料庫配置就參考輸入
其他轉換配置參考上面
其他看板參考上面的看板
4.4.4.2 開發商品分類數據抽取
4.3.3.3開發每周數據抽取作業
新建-作業-從通用裡面拖出組件-配置-連線
其他作業參考這個
4.3.4.1 開發商品表數據抽取
4.3.4.2 開發訂單數據抽取
4.3.4.3 開發用戶資訊表抽取
4.4.2 創建用於保存數據分析結果的表
--創建APP層的資料庫 CREATE DATABASE `it_shop_bi`; -- 訂單支付總額/總筆數分析 CREATE TABLE IF NOT EXISTS it_shop_bi.app_order_total( id INTEGER PRIMARY KEY AUTO_INCREMENT, -- 用作唯一記錄標識的組件,無實際意義 dt DATE, -- 對應的日期 total_money DOUBLE, -- 總支付金額 total_cnt INTEGER -- 總訂單筆數 ); SELECT SUBSTRING(t1.createTime, 1, 10) AS dt, -- 某一天 SUM(t1.realTotalMoney) AS total_money, -- 訂單總金額 COUNT(1) AS total_cnt -- 訂單總筆數 FROM ods_it_orders t1 WHERE SUBSTRING(t1.createTime, 1, 10) = '2019-09-05' GROUP BY SUBSTRING(t1.createTime, 1, 10); USE `ods_it_shop`; INSERT INTO it_shop_bi.app_order_total SELECT NULL, SUBSTRING(t1.createTime, 1, 10) AS dt, -- 某一天 SUM(t1.realTotalMoney) AS total_money, -- 訂單總金額 COUNT(1) AS total_cnt -- 訂單總筆數 FROM ods_it_orders t1 WHERE SUBSTRING(t1.createTime, 1, 10) = '2019-09-05' GROUP BY SUBSTRING(t1.createTime, 1, 10); USE ``; -- 訂單用戶分析表 CREATE TABLE IF NOT EXISTS it_shop_bi.app_order_user( id INTEGER PRIMARY KEY AUTO_INCREMENT, -- 唯一標識 dt DATE, -- 統計日期 total_user_cnt INTEGER -- 總用戶數 ); -- 統計每日下訂單用戶數 SELECT SUBSTRING(t1.createTime, 1, 10) AS dt, COUNT(DISTINCT t1.userId) AS user_total FROM `ods_it_shop`.ods_it_orders t1 WHERE SUBSTRING(t1.createTime, 1, 10) = '2019-09-05' GROUP BY SUBSTRING(t1.createTime, 1, 10); -- 統計每日下訂單用戶數 INSERT INTO `it_shop_bi`.app_order_user SELECT NULL, SUBSTRING(t1.createTime, 1, 10) AS dt, SUM(DISTINCT t1.userId) AS user_total FROM `ods_it_shop`.ods_it_orders t1 WHERE SUBSTRING(t1.createTime, 1, 10) = '2019-09-05' GROUP BY SUBSTRING(t1.createTime, 1, 10); -- 創建支付方式訂單總額/訂單筆數分析 CREATE TABLE IF NOT EXISTS `it_shop_bi`.app_order_paytype ( id INTEGER AUTO_INCREMENT PRIMARY KEY, -- 唯一標識(無意義) dt DATE, -- 統計日期 pay_type VARCHAR(20), -- 支付方式 total_money DOUBLE, -- 總支付金額 total_cnt INTEGER -- 總訂單筆數 ); -- 統計不同支付方式的訂單總金額、總筆數 SELECT '2019-09-05', CASE WHEN payType = 1 THEN '支付寶' WHEN payType = 2 THEN '微信' WHEN payType = 3 THEN '信用卡' ELSE '其他' END AS payType, SUM(t1.realTotalMoney) AS total_money, COUNT(1) AS total_cnt FROM `ods_it_shop`.ods_it_orders t1 WHERE SUBSTRING(dt,1,10) = '2019-09-05' GROUP BY t1.payType; -- 統計不同支付方式的訂單總金額、總筆數 INSERT INTO `it_shop_bi`.app_order_paytype SELECT NULL, '2019-09-05', CASE WHEN payType = 1 THEN '支付寶' WHEN payType = 2 THEN '微信' WHEN payType = 3 THEN '現金' ELSE '其他' END AS payType, SUM(t1.realTotalMoney) AS total_money, COUNT(1) AS total_cnt FROM `ods_it_shop`.ods_it_orders t1 WHERE SUBSTRING(dt,1,10) = '2019-09-05' GROUP BY t1.payType; -- 創建下訂單用戶最多的前5名 CREATE TABLE IF NOT EXISTS `it_shop_bi`.app_order_user_top5 ( id INTEGER AUTO_INCREMENT PRIMARY KEY,-- 唯一標識(無意義) dt VARCHAR(10),-- 統計日期 userid VARCHAR(20), -- 用戶id username VARCHAR(50), -- 用戶姓名 total_cnt INTEGER -- 總訂單筆數 ); -- 方式1 -- 統計訂單筆數TOP5用戶分析 SELECT '2019-09' , t.userId ,t.userName ,COUNT(orderId) AS total_cnt FROM `ods_it_shop`.ods_it_orders t WHERE SUBSTRING(createTime,1,7) = '2019-09' # 2019-09 GROUP BY t.userName, t.userId ORDER BY total_cnt DESC LIMIT 5; -- 統計訂單筆數TOP5用戶分析 INSERT INTO `it_shop_bi`.app_order_user_top5 SELECT NULL, '2019-09', userId, userName, COUNT(orderId) AS total_cnt FROM `ods_it_shop`.ods_it_orders WHERE SUBSTRING(createTime, 1, 7) = '2019-09' GROUP BY userId,userName ORDER BY total_cnt DESC LIMIT 5 -- 創建商品分類訂單總額/訂單筆數分析結果表 CREATE TABLE IF NOT EXISTS `it_shop_bi`.app_order_goods_cat ( id INTEGER AUTO_INCREMENT PRIMARY KEY,-- 唯一標識(無意義) dt DATE, -- 統計日期 cat_name VARCHAR(50), -- 一級分類名稱 total_money DOUBLE, -- 訂單總金額 total_num INTEGER -- 訂單總筆數 ); -- 統計不同一級商品分類訂單總額/總筆數分析 CREATE TABLE `it_shop_bi`.tmp_goods_cat AS SELECT t3.catId AS cat_id_l3, -- 3級分類id t3.catName AS cat_name_l3, -- 3級分類名稱 t2.catId AS cat_id_l2, -- 2級分類id t2.catName AS cat_name_l2, -- 2級分類名稱 t1.catId AS cat_id_l1, -- 1級分類id t1.catName AS cat_name_l1 -- 1級分類名稱 FROM `ods_it_shop`.ods_it_goods_cats t3, `ods_it_shop`.ods_it_goods_cats t2, `ods_it_shop`.ods_it_goods_cats t1 WHERE t3.parentId = t2.catId AND t2.parentId = t1.catId AND t3.cat_level = 3; SELECT * FROM `it_shop_bi`.tmp_goods_cat; -- 統計分析不同一級商品分類對應的總金額、總筆數 SELECT '2019-09-05', t1.cat_name_l1 AS goods_cat_l1, SUM(t3.payPrice * t3.goodsNum) AS total_money, COUNT(DISTINCT t3.orderId) AS total_cnt FROM `it_shop_bi`.tmp_goods_cat t1 LEFT JOIN `ods_it_shop`.ods_it_goods t2 ON t1.cat_id_l3 = t2.goodsCatId LEFT JOIN `ods_it_shop`.ods_it_order_goods t3 ON t2.goodsId = t3.goodsId WHERE SUBSTRING(t3.createTime, 1, 10) = '2019-09-05' GROUP BY t1.cat_name_l1; -- 執行跑了2.03秒 #性能優化 CREATE UNIQUE INDEX idx_goods_cat3 ON `it_shop_bi`.tmp_goods_cat(cat_id_l3); CREATE UNIQUE INDEX idx_it_goods ON `ods_it_shop`.ods_it_goods(goodsId); CREATE INDEX idx_it_order_goods ON `ods_it_shop`.ods_it_order_goods(goodsId); -- 優化後跑了0.038秒
SELECT NULL, '2019-09-05', t1.cat_name_l1 AS goods_cat_l1, SUM(t3.payPrice * t3.goodsNum) AS total_money, COUNT(DISTINCT t3.orderId) AS total_cnt FROM `it_shop_bi`.tmp_goods_cat t1 LEFT JOIN `ods_it_shop`.ods_it_goods t2 ON t1.cat_id_l3 = t2.goodsCatId LEFT JOIN `ods_it_shop`.ods_it_order_goods t3 ON t2.goodsId = t3.goodsId WHERE SUBSTRING(t3.createTime, 1, 10) = '2019-09-05' GROUP BY t1.cat_name_l1;
-- 分析某天的訂單總筆數、總金額 INSERT INTO `it_shop_bi`.app_order_total SELECT NULL, -- 對應無意義的唯一標識列 dt, -- 某一天 SUM(t1.realTotalMoney) AS total_money, -- 訂單總金額 COUNT(1) AS total_cnt -- 訂單總筆數 FROM `ods_it_shop`.ods_it_orders t1 WHERE t1.dt = '${dt}'; -- 統計每日下訂單用戶數 INSERT INTO it_shop_bi.app_order_user SELECT NULL, dt, SUM(DISTINCT t1.userId) FROM `ods_it_shop`.ods_it_orders t1 WHERE t1.dt = '${dt}'; -- 統計不同支付方式的訂單總金額、總筆數 INSERT INTO `it_shop_bi`.app_order_paytype SELECT NULL, dt, CASE WHEN payType = 1 THEN '支付寶' WHEN payType = 2 THEN '微信' WHEN payType = 3 THEN '現金' ELSE '其他' END AS payType, SUM(t1.realTotalMoney) AS total_money, COUNT(1) AS total_cnt FROM `ods_it_shop`.ods_it_orders t1 WHERE dt = '${dt}' GROUP BY t1.payType; -- 統計不同一級商品分類訂單總額/總筆數分析 DROP TABLE IF EXISTS `it_shop_bi`.tmp_goods_cat; CREATE TEMPORARY TABLE `it_shop_bi`.tmp_goods_cat AS SELECT t3.catId AS cat_id_l3, -- 3級分類id t3.catName AS cat_name_l3, -- 3級分類名稱 t2.catId AS cat_id_l2, -- 2級分類id t2.catName AS cat_name_l2, -- 2級分類名稱 t1.catId AS cat_id_l1, -- 1級分類id t1.catName AS cat_name_l1 -- 1級分類名稱 FROM `ods_it_shop`.ods_it_goods_cats t3, `ods_it_shop`.ods_it_goods_cats t2, `ods_it_shop`.ods_it_goods_cats t1 WHERE t3.parentId = t2.catId AND t2.parentId = t1.catId AND t3.cat_level = 3; -- 在商品分類臨時表上添加索引 CREATE UNIQUE INDEX idx_goods_cat3 ON `it_shop_bi`.tmp_goods_cat (cat_id_l3); -- 查看臨時表中的數據 SELECT * FROM `it_shop_bi`.tmp_goods_cat; INSERT INTO `it_shop_bi`.app_order_goods_cat SELECT NULL, t3.dt, t1.cat_name_l1 AS goods_cat_l1, SUM(t3.payPrice * t3.goodsNum) AS total_money, COUNT(DISTINCT t3.orderId) AS total_cnt FROM `it_shop_bi`.tmp_goods_cat t1 LEFT JOIN `ods_it_shop`.ods_it_goods t2 ON t1.cat_id_l3 = t2.goodsCatId LEFT JOIN `ods_it_shop`.ods_it_order_goods t3 ON t2.goodsId = t3.goodsId WHERE t3.dt = '${dt}' GROUP BY t1.cat_name_l1; -- 統計訂單筆數TOP5用戶分析 INSERT INTO `it_shop_bi`.app_order_user_top5 SELECT NULL, SUBSTRING(CURDATE(), 1, 7) AS today, userId, userName, COUNT(orderId) AS total_cnt FROM `ods_it_shop`.ods_it_orders t2 WHERE SUBSTRING(createTime, 1, 7) IS NOT NULL GROUP BY userId,userName ORDER BY total_cnt DESC LIMIT 5
4.9 開發Kettle作業
5.使用Apache Superset進行數據可視化
cd cd /root/anaconda3/ superset run -h 192.168.88.100 -p 8080 –with-threads –reload –debugger |
- 登錄superset
- //192.168.88.100:8080/superset/welcome
- 用戶名: admin
- 密碼:123456
1.1.1 添加MySQL資料庫
mysql://root:[email protected]/it_shop_bi?charset=utf8 |
1.1 訂單支付方式分析開發
5.7 訂單商品分類分析
5.8 Superset Dashboard(看板)開發