MySQL计算同比和环比

MySQL计算同比和环比

今天在做数据建模的时候,ads层的需求中有个叫同比和环比的指标,这两指标的计算之前都没有接触过,经过我两三个小时的研究,终于搞明白什么是同比,什么是环比,如何使用Mysql来计算同比和环比。

我们先来看看什么是同比,什么是环比:

  • 同比:通常是指今年第n月与去年第n月相比。同比发展速度主要是为了消除季节变动的影响,用以说明本期发展水平与去年同期发展水平对比而达到的相对发展速度。

  • 环比:通常是指表示连续2个单位周期(比如连续两月)内的量的变化比。环比包括两种:环比增长速度和环比发展速度。

那同比增长率和环比增长率又如何计算呢:

同比增长率:
同 比 增 长 率 = ( 本 期 数 − 同 期 数 ) / 同 期 数 ∗ 100 % 同比增长率=(本期数-同期数)/同期数*100 \% =()/100%

环比增长率:
环 比 增 长 率 = ( 本 期 数 − 上 期 数 ) / 上 期 数 × 100 % 环比增长率=(本期数-上期数)/上期数×100\% =()/×100%

在MySQL中如何计算同比和环比

数据准备

创建商品表并向商品表中添加数据
CREATE TABLE product  (
  `产品ID` varchar(20) NOT NULL,
	  `产品名称` varchar(20) ,
  `产品单价` int (10)
)
INSERT INTO product VALUES ('C1001','产品A',45);
INSERT INTO product VALUES ('C1002','产品B',52);
INSERT INTO product VALUES ('C1003','产品C',39);

image-20220317095350813

创建订单明细表并向订单明细表中添加数据
CREATE TABLE sales  (
    `订单ID` int NOT NULL PRIMARY KEY AUTO_INCREMENT,
  `产品ID` varchar(25) NOT NULL ,
	  `销售数量` int(20) ,
  `销售时间` timestamp(6) NULL DEFAULT NULL
);
INSERT INTO sales(`产品ID`,`销售数量`,`销售时间`) VALUES ('C1001', 15, '2020-06-01 10:10:12');
INSERT INTO sales(`产品ID`,`销售数量`,`销售时间`)  VALUES ('C1002',26, '2020-05-02 0:10:12');
INSERT INTO sales (`产品ID`,`销售数量`,`销售时间`) VALUES ('C1003', 21, '2020-04-03 0:10:12');
INSERT INTO sales (`产品ID`,`销售数量`,`销售时间`) VALUES ('C1003', 23, '2020-04-04 0:10:12');
INSERT INTO sales (`产品ID`,`销售数量`,`销售时间`) VALUES ('C1003', 0, '2020-03-05 0:10:12');
INSERT INTO sales (`产品ID`,`销售数量`,`销售时间`) VALUES ('C1001', 16, '2020-02-06 3:0:12');
INSERT INTO sales (`产品ID`,`销售数量`,`销售时间`) VALUES ('C1002', 32, '2020-01-07 0:10:12');
INSERT INTO sales (`产品ID`,`销售数量`,`销售时间`) VALUES ('C1001', 16, '2019-12-08 0:12:24');
INSERT INTO sales (`产品ID`,`销售数量`,`销售时间`) VALUES ('C1001', 32, '2019-06-09  0:12:24');
INSERT INTO sales (`产品ID`,`销售数量`,`销售时间`) VALUES ('C1002', 17, '2019-05-09 0:12:24');

image-20220317121540101

计算同比和环比

select  year(c.销售时间) yy,month(c.销售时间) mm,     
       sum(c.销售数量*d.产品单价) ss,
concat(ifnull(abs(round((sum(c.销售数量*d.产品单价)-ss1)/ss1*100,2)),0),'%') 同比,
concat(ifnull(abs(round((sum(c.销售数量*d.产品单价)-ss2)/ss2*100,2)),0),'%')  环比
from sales c
left join product d on c.产品ID=d.产品ID
left join (select month(a.销售时间) mm1,
                    year(a.销售时间) yy1,
                    sum(a.销售数量*d.产品单价) ss1
          from sales a
          left join product d on a.产品ID=d.产品ID
          GROUP BY mm1,yy1) a
          on month(c.销售时间) = a.mm1 
          and a.yy1 = year(c.销售时间)-1	 
 left join  (select month(a.销售时间) mm2,
                    year(a.销售时间) yy2,
                    sum(a.销售数量*d.产品单价) ss2
             from sales a
		     left join product d on a.产品ID=d.产品ID
              GROUP BY mm2,yy2) b
on (b.yy2 = year(c.销售时间) and b.mm2+1 = month(c.销售时间) OR (yy2=year(c.销售时间)-1 
AND b.mm2 = 12 AND month(c.销售时间) = 1))
 group by yy, mm
 order by yy,mm asc

sql解析

select  year(c.销售时间) yy,month(c.销售时间) mm,     
       sum(c.销售数量*d.产品单价) ss,
 
# concat函数,mysql字符串拼接,因为同比和环比都是百分数
# ifnull函数,mysql判断字段是否为空,为空则为0
# abs函数,mysql取绝对值,因为我这里取的都是正数
# round函数,mysql保留几位小数
concat(ifnull(abs(round((sum(c.销售数量*d.产品单价)-ss1)/ss1*100,2)),0),'%') 同比,
concat(ifnull(abs(round((sum(c.销售数量*d.产品单价)-ss2)/ss2*100,2)),0),'%')  环比
from sales c
left join product d on c.产品ID=d.产品ID
 
# 上一年销售额
left join (select month(a.销售时间) mm1,
                    year(a.销售时间) yy1,
                    sum(a.销售数量*d.产品单价) ss1
          from sales a
          left join product d on a.产品ID=d.产品ID
          GROUP BY mm1,yy1) a
          # 同比月份相同,年份减1
          on month(c.销售时间) = a.mm1   
          and a.yy1 = year(c.销售时间)-1	
 
# 今年销售额 
 left join  (select month(a.销售时间) mm2,
                    year(a.销售时间) yy2,
                    sum(a.销售数量*d.产品单价) ss2
             from sales a
		     left join product d on a.产品ID=d.产品ID
              GROUP BY mm2,yy2) b
          # 环比取数考虑到为一月的情况
on (b.yy2 = year(c.销售时间) and b.mm2+1 = month(c.销售时间) OR (yy2=year(c.销售时间)-1 
AND b.mm2 = 12 AND month(c.销售时间) = 1))
 group by yy, mm
 order by yy,mm asc

结果:

image-20220317121641787

除此之外,还想要计算累计销售的话,可以这样写:

select year(销售时间) yy,month(销售时间) mm,
sum(销售数量*b.产品单价) over(order by year(销售时间) ,month(销售时间) ) 累计数量 
from sales a
left join product b on a.产品ID=b.产品ID
order by  yy,mm

同比和环比这块的确不好计算

### Java 中实现同比环比计算的方法 #### 环比计算的概念 环比计算是指与上一个周期的数据进行比较,通常用于衡量短期内的变化趋势。这种计算方式能够帮助分析师迅速捕捉到数据波动的情况。 #### 同比计算的概念 同比计算则是指当前周期与去年同期的数据对比,主要用于消除季节性因素的影响,提供更长期的趋势分析视角。 #### 使用 `BigDecimal` 进行精确计算 为了确保计算精度,在处理金融或统计数据时推荐使用 `BigDecimal` 来代替原始浮点数类型。这可以有效避免由于浮点运算带来的舍入误差问题[^3]。 ```java import java.math.BigDecimal; import java.math.RoundingMode; public class RatioCalculator { /** * 计算两个数值之间的环比增长率 * * @param current 当前期数值 * @param previous 上一期数值 * @return 返回环比增长百分比 */ public static BigDecimal calculateSequentialGrowth(double current, double previous) { if (previous == 0) throw new ArithmeticException("Previous value cannot be zero"); BigDecimal cur = BigDecimal.valueOf(current); BigDecimal prev = BigDecimal.valueOf(previous); return BigDecimal.valueOf(100) .multiply(cur.subtract(prev)) .divide(prev, 2, RoundingMode.HALF_DOWN); } /** * 计算两个相同月份不同年份间的同比增长率 * * @param currentValue 当前期数值 * @param lastYearValue 去年同期数值 * @return 返回同比增长百分比 */ public static BigDecimal calculateYearOnYearGrowth(double currentValue, double lastYearValue) { if (lastYearValue == 0) throw new ArithmeticException("Last year's value cannot be zero"); BigDecimal curVal = BigDecimal.valueOf(currentValue); BigDecimal lyVal = BigDecimal.valueOf(lastYearValue); return BigDecimal.valueOf(100) .multiply(curVal.subtract(lyVal)) .divide(lyVal, 2, RoundingMode.HALF_DOWN); } } ``` 上述代码展示了如何利用 `BigDecimal` 对象来进行同比环比的增长率计算,并通过设置合适的舍入模式来保证最终结果的小数位数准确性。 #### 处理日期逻辑 对于时间序列数据而言,获取正确的前后期间是非常重要的一步。Java 提供了丰富的 API 支持这一需求: - 可以借助 `LocalDate`, `MonthDay` 或者其他相关类从给定日期推导出相应的去年同月/日以及上个月的具体日期; - 如果涉及到跨年的特殊情况,则需特别注意调整月份边界条件。 ```java import java.time.LocalDate; import java.time.temporal.TemporalAdjusters; // 获取指定日期所在月份的第一天对应的去年同一时刻 private LocalDate getSameDayOfLastYear(LocalDate date){ return date.with(TemporalAdjusters.firstDayOfMonth()) .minusYears(1L); } // 获取指定日期所在的上一个月最后一天 private LocalDate getLastMonthEnd(LocalDate date){ return date.minusMonths(1L) .with(TemporalAdjusters.lastDayOfMonth()); } ``` 以上函数可以帮助开发者轻松定位所需的时间节点以便于后续的数据提取工作[^2]。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我菜的要死

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值