分类课程智能体AI
文章
订阅
分类课程AI导师
文章
价格
课程进度
5 / 12
上一节把四张表连接成销售明细下一节沿着时间轴观察经营变化
自在学

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

公网安备湘公网安备43020302000292号 | 湘ICP备2025148919号-1

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

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

公网安备湘公网安备43020302000292号湘ICP备2025148919号-1

编程SQL + Excel 数据分析实战建立可复核的经营指标

建立可复核的经营指标

指标不是给字段换一个名字。每个指标都包含分析范围、数据粒度和计算公式。这一节先把订单明细聚合到订单粒度,再计算订单数、客户数、销售额、客单价、毛利和毛利率。

开始写 SQL 前,先把指标口径写成一张小字典。这样做看似多了一步,实际能避免“同名指标、不同算法”在 SQL 和 Excel 之间来回出现。

指标统计对象分子分母本项目过滤条件
完成订单数订单——只统计完成订单
客户数客户——至少有一笔完成订单
销售额订单明细数量 × 成交单价之和—只统计完成订单
客单价订单销售额完成订单数退款订单不进入分子和分母
毛利率订单明细销售额 − 数量 × 成本销售额用金额加权,不平均行毛利率

“统计对象”就是指标粒度。订单数的对象是订单,销售额的原始对象是订单明细;如果不先说明这一点,同一个订单含三件商品时,就很容易被误算成三笔订单。


先把明细聚合成订单

sql
WITH order_metrics AS (
    SELECT
        order_id,
        customer_id,
        order_date,
        SUM(quantity) AS units,
        ROUND(SUM(revenue), 2) AS order_revenue,
        ROUND(SUM(gross_profit), 2) AS order_profit
    FROM v_sales_detail
    GROUP BY order_id, customer_id, order_date
)
SELECT *
FROM order_metrics
ORDER BY order_date, order_id
LIMIT 10;

CTE order_metrics 的粒度是一行一订单。后面算客单价时,分母直接使用 COUNT(*),不会被订单中的商品行数干扰。

可以先做一个小检查,确认 CTE 真的是一行一订单:

sql
WITH order_metrics AS (
    SELECT order_id
    FROM v_sales_detail
    GROUP BY order_id, customer_id, order_date
)
SELECT
    COUNT(*) AS rows_in_cte,
    COUNT(DISTINCT order_id) AS distinct_orders
FROM order_metrics;

两列都应是 509。若 rows_in_cte 更大,说明分组列让同一订单被拆开了;这时不要继续算客单价,先回头检查订单是否跨客户、跨日期,或是否误把 product_id 放进了分组。


一次算出核心指标

sql
WITH order_metrics AS (
    SELECT
        order_id,
        customer_id,
        SUM(revenue) AS order_revenue,
        SUM(gross_profit) AS order_profit
    FROM v_sales_detail
    GROUP BY order_id, customer_id
)
SELECT
    COUNT(*) AS orders,
    COUNT(DISTINCT customer_id) AS customers,
    ROUND(SUM(order_revenue), 2) AS revenue,
    ROUND(AVG(order_revenue), 2) AS avg_order_value,
    ROUND(SUM(order_profit), 2) AS gross_profit,
    ROUND(SUM(order_profit) / SUM(order_revenue), 4) AS gross_margin
FROM order_metrics;
orderscustomersrevenueavg_order_valuegross_profitgross_margin
509120229909.45451.69113296.450.4928

客单价是订单销售额的平均值:

客单价=销售额完成订单数\text{客单价}=\frac{\text{销售额}}{\text{完成订单数}}客单价=完成订单数销售额​

毛利率使用总毛利除以总销售额,不是逐行毛利率的平均值:

毛利率=∑毛利∑销售额\text{毛利率}=\frac{\sum\text{毛利}}{\sum\text{销售额}}毛利率=∑销售额∑毛利​

如果先计算每件商品的毛利率再取平均,小额商品和大额商品会得到相同权重,结果会偏离整体经营情况。

举个极端例子:商品 A 销售额 100 元、毛利率 10%,商品 B 销售额 900 元、毛利率 50%。直接平均得到 30%,但真实总毛利是 460 元,总销售额是 1000 元,因此整体毛利率是 46%。经营指标需要回答“每 1 元销售额留下多少毛利”,所以必须按金额加权。

查询结果中的 0.4928 是小数比例。SQL 中保留小数,交给 Excel 设置为 49.28%;不要在 SQL 中先乘 100 又在 Excel 中设为百分比,否则会显示成 4928%。


按城市拆解指标

sql
SELECT
    city,
    COUNT(DISTINCT order_id) AS orders,
    COUNT(DISTINCT customer_id) AS customers,
    ROUND(SUM(revenue), 2) AS revenue,
    ROUND(
        SUM(revenue) / COUNT(DISTINCT order_id),
        2
    ) AS avg_order_value
FROM v_sales_detail
GROUP BY city
ORDER BY revenue DESC;
cityorderscustomersrevenueavg_order_value
上海1503565749.75438.33
北京1333460498.39454.88
广州761735442.30466.35
深圳751734390.22458.54
杭州751733828.79451.05

上海销售额最高,主要原因是订单更多;广州客单价更高,但订单规模较小。一个总量指标和一个效率指标放在一起,解释才不会停在“谁排名第一”。

这里可以用一个恒等式检查解释是否成立:

销售额=订单数×客单价\text{销售额}=\text{订单数}\times\text{客单价}销售额=订单数×客单价

例如用表格中的显示值计算,上海约为 150 × 438.33 = 65749.50,与 65749.75 相差 0.25 元。这个小差异来自表格中客单价只显示两位小数,而 SQL 内部仍使用完整精度,不是数据错误。若差异很大,再检查订单数是否用了 DISTINCT、客单价是否和销售额使用同一筛选范围。


按渠道比较客户质量

sql
WITH customer_channel AS (
    SELECT
        channel,
        customer_id,
        COUNT(DISTINCT order_id) AS orders,
        SUM(revenue) AS revenue
    FROM v_sales_detail
    GROUP BY channel, customer_id
)
SELECT
    channel,
    COUNT(*) AS customers,
    ROUND(AVG(orders), 2) AS orders_per_customer,
    ROUND(AVG(revenue), 2) AS revenue_per_customer
FROM customer_channel
GROUP BY channel
ORDER BY revenue_per_customer DESC;

这里先算“每位客户在一个渠道中的表现”,再按渠道求平均。如果直接对销售明细求平均,购买商品多的客户会产生更多行,从而获得更高权重。

这段 SQL 有两个粒度:customer_channel 是“一行一渠道一客户”,外层查询才是“一行一渠道”。阅读多层 SQL 时,可以在每个 CTE 旁边写一句粒度说明。只要某一层的粒度说不清,就先不要继续往下聚合。

还要注意,这里的客户数不能跨渠道相加。一位客户可能同时在小程序和门店购买,渠道客户数相加会重复;如果要汇报全局客户数,仍应单独使用 COUNT(DISTINCT customer_id)。


加一条总额对账

分组分析完成后,把各城市销售额相加,应该仍然等于 229909.45。

sql
WITH city_metrics AS (
    SELECT city, SUM(revenue) AS revenue
    FROM v_sales_detail
    GROUP BY city
)
SELECT ROUND(SUM(revenue), 2) AS checked_revenue
FROM city_metrics;

这类对账很简单,却能及时发现连接重复、筛选范围变化或分组遗漏。下一节把指标放到时间轴上,观察月度变化和趋势。

完成这一节前,按下面的顺序验收:

  1. order_metrics 的行数与去重订单数都等于 509。
  2. 全局销售额等于 229909.45,毛利等于 113296.45。
  3. 城市销售额加总仍等于全局销售额。
  4. 客单价使用订单粒度,毛利率使用总毛利除以总销售额。
  5. 分组后的客户数只在组内解释,不把可能重叠的客户数直接相加。

只有这些检查都通过,下一节的月度环比才有可信的基准。

  • 先把明细聚合成订单
  • 一次算出核心指标
  • 按城市拆解指标
  • 按渠道比较客户质量
  • 加一条总额对账

目录

  • 先把明细聚合成订单
  • 一次算出核心指标
  • 按城市拆解指标
  • 按渠道比较客户质量
  • 加一条总额对账