自在学

我们与你共同进步

  • 分类课程
  • 文章
  • 工作台
  • 订阅

  • 关于我们
  • 隐私政策
  • 使用条款

探索

  • 分类课程
  • 文章
  • 工作台
  • 订阅

网站信息

  • 关于我们
  • 隐私政策
  • 使用条款

加入社区

自在学学习社区微信二维码

微信扫码,交流学习

株洲市自在学教育科技有限公司© 2025 - 2026 版权所有

© 2025 - 2026 株洲市自在学教育科技有限公司 版权所有

湘公网安备43020302000292号|湘ICP备2025148919号-1
分类课程工作台文章订阅
分类课程工作台文章价格

SQL + Excel 数据分析实战

  1. 01从业务问题到第一张数据表
  2. 02先把数据质量说清楚
  3. 03用 SQL 回答第一个业务问题
  4. 04把四张表连接成销售明细
  5. 05建立可复核的经营指标
  6. 06沿着时间轴观察经营变化
  7. 07从品类贡献到商品组合
  8. 08用 RFM 看懂客户价值
  9. 09用队列留存回答客户会不会回来
  10. 10让 SQL 结果成为 Excel 的可刷新数据源
  11. 11在 Excel 中复核指标并完成三张图
  12. 12把分析整理成一页可刷新的经营看板
正在加载课程章节内容
课程编程SQL + Excel 数据分析实战从品类贡献到商品组合

从品类贡献到商品组合

商品分析先回答“卖得怎么样”,再回答“哪些商品经常一起出现”。前者看销售额、销量和毛利,后者看购物篮中的共现关系。


比较品类的规模与效率

sql
SELECT
    category,
    COUNT(DISTINCT order_id) AS orders,
    SUM(quantity) AS units,
    ROUND(SUM(revenue), 2) AS revenue,
    ROUND(SUM(gross_profit), 2) AS gross_profit,
    ROUND(
        SUM(gross_profit) / SUM(revenue),
        4
    ) AS gross_margin
FROM v_sales_detail
GROUP BY category
ORDER BY revenue DESC;
categoryordersunitsrevenuegross_profitgross_margin
数码21246868927.6034149.600.4954
餐厨25363660879.5529519.550.4849
居家25555243051.2621234.260.4932
饮水21146233559.8117123.810.5102
出行21242523491.2311269.230.4797

数码销售额最高,饮水毛利率最高。两个结论并不冲突:销售额描述规模,毛利率描述每元销售额留下多少毛利。

把品类放进“规模 × 效率”矩阵会更容易决定下一步:

规模毛利率解释重点常见动作
高高核心贡献品类保证供给,避免无效折扣
高低有销量但利润承压检查成本、折扣和商品结构
低高有效率但规模不足测试曝光、组合和适用人群
低低贡献与效率都弱缩减资源,先验证保留理由

高、低的分界可以用中位数、预算目标或历史基线。本项目先不强行贴标签,避免用很小的数据集做过度决策;我们先掌握方法,再结合业务目标设阈值。


找出具体商品的贡献

sql
SELECT
    product_name,
    category,
    COUNT(DISTINCT order_id) AS orders,
    SUM(quantity) AS units,
    ROUND(SUM(revenue), 2) AS revenue,
    ROUND(SUM(gross_profit), 2) AS gross_profit
FROM v_sales_detail
GROUP BY product_id, product_name, category
ORDER BY revenue DESC;

把 product_id 放进分组,是为了避免未来出现同名商品时被合并。展示时可以不显示编号,但计算时要使用稳定键。

商品排名还要同时看订单数与销量。某商品销售额高,可能因为单价高而不是买家多;销量高也可能来自一笔批量订单。orders、units、revenue 和 gross_profit 放在同一行,正是为了把这些可能性分开。


把订单变成购物篮

购物篮分析只关心某件商品是否出现在订单中。同一订单里买两件相同商品,仍然只算一次出现,所以先去重:

sql
WITH baskets AS (
    SELECT DISTINCT
        order_id,
        product_id,
        product_name
    FROM v_sales_detail
)
SELECT *
FROM baskets
ORDER BY order_id, product_id
LIMIT 20;

然后让购物篮表和自己连接,找同一订单里的两件不同商品:

sql
WITH baskets AS (
    SELECT DISTINCT order_id, product_id, product_name
    FROM v_sales_detail
)
SELECT
    a.product_name AS product_a,
    b.product_name AS product_b,
    COUNT(*) AS pair_orders
FROM baskets AS a
JOIN baskets AS b
  ON a.order_id = b.order_id
 AND a.product_id < b.product_id
GROUP BY
    a.product_id,
    a.product_name,
    b.product_id,
    b.product_name
ORDER BY pair_orders DESC;

条件 a.product_id < b.product_id 同时避免“商品和自己配对”以及“A+B、B+A”重复计数。

为什么第一步必须 DISTINCT?假设订单 O001 中咖啡壶分两行记录、收纳盒分三行记录。若直接连接明细,两种商品会形成 2 × 3 = 6 行组合,COUNT(*) 会把一个购物篮膨胀成多次共现;去重后,每件商品在一个订单中只有一次存在记录,共现次数才表示“多少笔订单同时包含它们”。


计算支持度、置信度和提升度

支持度表示组合占全部订单的比例;置信度表示买了 A 的订单中有多少也买了 B;提升度把这条置信度与 B 的自然出现概率比较。

支持度(A,B)=同时包含 A 与 B 的订单数总订单数\text{支持度}(A,B)=\frac{\text{同时包含 A 与 B 的订单数}}{\text{总订单数}}支持度(A,B)=总订单数同时包含 A 与 B 的订单数​ 置信度(A→B)=同时包含 A 与 B 的订单数包含 A 的订单数\text{置信度}(A\rightarrow B)=\frac{\text{同时包含 A 与 B 的订单数}}{\text{包含 A 的订单数}}置信度(A→B)=包含 A 的订单数同时包含 A 与 B 的订单数​ 提升度(A→B)=置信度(A→B)包含 B 的订单占比\text{提升度}(A\rightarrow B)=\frac{\text{置信度}(A\rightarrow B)}{\text{包含 B 的订单占比}}提升度(A→B)=包含 B 的订单占比置信度(A→B)​
sql
WITH baskets AS (
    SELECT DISTINCT order_id, product_id, product_name
    FROM v_sales_detail
), pairs AS (
    SELECT
        a.product_id AS product_a_id,
        a.product_name AS product_a,
        b.product_id AS product_b_id,
        b.product_name AS product_b,
        COUNT(*) AS pair_orders
    FROM baskets AS a
    JOIN baskets AS b
      ON a.order_id = b.order_id
     AND a.product_id < b.product_id
    GROUP BY
        a.product_id, a.product_name,
        b.product_id, b.product_name
    HAVING COUNT(*) >= 30
), product_orders AS (
    SELECT
        product_id,
        COUNT(DISTINCT order_id) AS orders_with_product
    FROM baskets
    GROUP BY product_id
), totals AS (
    SELECT COUNT(DISTINCT order_id) AS total_orders
    FROM baskets
)
SELECT
    p.product_a,
    p.product_b,
    p.pair_orders,
    ROUND(1.0 * p.pair_orders / t.total_orders, 4) AS support,
    ROUND(1.0 * p.pair_orders / pa.orders_with_product, 4) AS confidence,
    ROUND(
        (1.0 * p.pair_orders / pa.orders_with_product)
        / (1.0 * pb.orders_with_product / t.total_orders),
        4
    ) AS lift
FROM pairs AS p
JOIN product_orders AS pa ON pa.product_id = p.product_a_id
JOIN product_orders AS pb ON pb.product_id = p.product_b_id
CROSS JOIN totals AS t
WHERE
    (1.0 * p.pair_orders / pa.orders_with_product)
    / (1.0 * pb.orders_with_product / t.total_orders) > 1.20
ORDER BY lift DESC, pair_orders DESC;

例如“手冲咖啡壶 + 收纳盒”共同出现在 85 笔订单中,支持度约 0.167,提升度约 4.072。提升度大于 1 表示两件商品一起出现的程度高于随机预期,但这仍然是关联,不是因果。

用 100 笔订单做一次手算:40 笔含 A,20 笔含 B,12 笔同时含 A 和 B。

支持度=12/100=12%\text{支持度}=12/100=12\%支持度=12/100=12% 置信度(A→B)=12/40=30%\text{置信度}(A\rightarrow B)=12/40=30\%置信度(A→B)=12/40=30% 提升度(A→B)=30%/20%=1.5\text{提升度}(A\rightarrow B)=30\%/20\%=1.5提升度(A→B)=30%/20%=1.5

这表示购买 A 的订单出现 B 的概率,是 B 在全部订单中自然出现概率的 1.5 倍。置信度有方向:A → B 的分母是 40,而 B → A 的分母是 20,所以后者是 60%;提升度在二元共现中数值相同,但推荐入口和可触达人数不同。


把规则变成可执行条件

实际推荐不能只按提升度排序。支持度太低的组合可能只是偶然。上面的完整查询已经在 pairs 中用 HAVING COUNT(*) >= 30 限制共同订单数,并在最终结果中只保留提升度大于 1.20 的组合。

阈值应随订单规模调整。订单量扩大后,可以提高最低共同订单数;如果组合太少,则先检查商品类别与分析时间范围。

在把组合放进推荐位之前,再做三层检查:

  1. 规模检查:共同订单数是否过少,是否还需要更多样本和稳定性检验。本项目先用 30 笔作为演示门槛,它不能保证关联不是偶然。
  2. 稳定性检查:把数据分成前后两个时期,组合是否都存在,而不是只由一次活动造成。
  3. 经营检查:组合后的毛利是否为正、库存是否充足、两件商品是否真的适合一起使用。

可以用下面的查询查看组合在不同月份是否稳定:

sql
WITH baskets AS (
    SELECT DISTINCT order_id, order_month, product_id
    FROM v_sales_detail
), monthly_pairs AS (
    SELECT
        a.order_month,
        a.product_id AS product_a_id,
        b.product_id AS product_b_id,
        COUNT(*) AS pair_orders
    FROM baskets AS a
    JOIN baskets AS b
      ON a.order_id = b.order_id
     AND a.product_id < b.product_id
    GROUP BY a.order_month, a.product_id, b.product_id
)
SELECT *
FROM monthly_pairs
WHERE pair_orders >= 3
ORDER BY product_a_id, product_b_id, order_month;

这张月度表不直接证明推荐有效,只用于判断关联是否持续。真正上线组合推荐后,还要通过对照实验比较转化率、连带率和组合毛利。下一节转向客户,把购买日期、频次和金额整合成可维护的客户分群。


换一组数据自己核对

下面的练习使用小型变式。请先按正文的粒度和口径作答,再展开核对。

练习 1:区分件数与购物篮存在

订单 1 的 A 商品分两行、B 商品分三行记录;订单 2 只有 A。一笔订单里商品买了多件,与同一商品分多行有什么不同?去重前后 A+B 共现计数是多少?

quantity 增加只改变件数,不自动增加明细行。分行记录会让订单 1 的自连接形成 2 × 3 = 6 行;对 order_id、product_id 去重后只算订单 1 一次,共现订单数为 1。编号小于条件同时避免自配对与反向重复。

练习 2:用同一交易集计算三种指标

80 笔订单,20 笔含 A、40 笔含 B、10 笔同时含 A 和 B。计算支持度、A→B 与 B→A 置信度和提升度。

支持度 10/80 = 12.5%;A→B 置信度 10/20 = 50%;B→A 为 10/40 = 25%。提升度 0.5/(40/80) = 1,反向也是 1。A 出现后的 B 概率和 B 的自然概率一样,这里没有额外正向关联。

练习 3:分清频率阈值、稳定性与因果

组合甲共现 5 笔、提升度 8;组合乙共现 35 笔、提升度 1.3。按本节共同订单至少 30、提升度大于 1.20 的门槛保留哪个?能据此断言推荐位会增加毛利吗?

只保留乙。甲提升度虽高但规模不足;乙满足演示筛选门槛,也还要检查不同时期、库存和毛利。共现只能说明关联,推荐效果必须通过对照实验验证。

练习 4:区分品类贡献、件数和单位效率

品类甲 2 笔订单卖出 10 件,销售额 200 元、毛利 40 元;品类乙 4 笔订单卖出 4 件,销售额 400 元、毛利 60 元。谁的销售额、件数和毛利率更高?同一订单买两个品类时,各品类订单数能直接加成全局订单数吗?

乙销售额更高,甲件数更多;甲毛利率为 40/200=20%,乙为 60/400=15%。同一订单可以同时属于两个品类,各组订单数可能重叠,不能相加成全局订单数。规模、件数、效率分别回答不同问题。

上一章沿着时间轴观察经营变化下一章用 RFM 看懂客户价值