【问题标题】:Calculating rolling percentage change by category in MySQL 5.6在 MySQL 5.6 中按类别计算滚动百分比变化
【发布时间】:2022-01-12 17:45:10
【问题描述】:

我有一个这样的 SQL 数据表,我想计算滚动百分比变化(按行和类别)。所以结果如下所示

我使用的 SQL 查询真的很慢,当有数千个类别时,它需要很长时间才能计算出来,你知道发生了什么吗?或者有什么改进?

首先创建一个示例data_table:

CREATE TABLE IF NOT EXISTS data_table (
    id INT AUTO_INCREMENT,
    num INT,
    category VARCHAR(10),
    price FLOAT(20,2),
    PRIMARY KEY (id)
);

INSERT INTO data_table(num,category,price)
VALUES(1,"A","10"),
      (2,"A","20"),
      (3,"A","30"),
      (1,"B","20"),
      (2,"B","30"),
      (3,"B","40");

用于计算百分比变化的 SQL:

SELECT 
     A.*, 
     CASE WHEN (A.price IS NULL OR B.price IS NULL OR B.price=0) THEN 0 ELSE
        (A.price - B.price)/(B.price) *100 END AS perc
FROM (SELECT
    num,
    category,
    price
  FROM data_table
  ) A LEFT JOIN (SELECT
    num,
    category,
    price
  FROM data_table
  ) B
ON (A.num = B.num+1) AND A.category=B.category;

【问题讨论】:

  • 请添加 CREATE TABLE 语句并将数据的图像替换为 markdown 表和/或 INSERT 语句。
  • 我刚刚添加了用于创建数据的 SQL @nnichols
  • 在 (category, num) 上添加索引可能会有所帮助。
  • 升级到 MySQL 后,将提供使用 LAG() 的不同解决方案。

标签: mysql sql mysql-5.7 mysql-5.6


【解决方案1】:

你可以使用用户变量 -

SELECT
    dt.*,
    IF(@prev_cat <> category, NULL, ROUND((price - @prev_price) / @prev_price * 100, 1)) AS perc,
    @prev_cat := category,
    @prev_price := price
FROM data_table dt, (SELECT @prev_cat := 0, @prev_price := 0) vars
ORDER BY category, num;

如果你想用这个 perc 值更新你的表,你可以使用 -

ALTER TABLE `data_table` 
  ADD COLUMN `perc` DECIMAL(5,2) NULL AFTER `price`;

UPDATE `test`.`data_table` dt
JOIN (
    SELECT
        dt.*,
        IF(@prev_cat <> category, NULL, ROUND((price - @prev_price) / @prev_price * 100, 1)) AS perc_calc,
        @prev_cat := category,
        @prev_price := price
    FROM data_table dt, (SELECT @prev_cat := 0, @prev_price := 0) vars
    ORDER BY category, num
) z ON dt.id = z.id
SET dt.perc = z.perc_calc;

如果您使用的是 MySQL 8,那么使用 LAG() 会更容易一些 -

SELECT dt.*,
    ROUND((price - LAG(price, 1) OVER (PARTITION BY category ORDER BY num ASC)) / LAG(price, 1) OVER (PARTITION BY category ORDER BY num ASC) * 100, 1) AS `prev1`,
    ROUND((price - LAG(price, 2) OVER (PARTITION BY category ORDER BY num ASC)) / LAG(price, 2) OVER (PARTITION BY category ORDER BY num ASC) * 100, 1) AS `prev2`,
    ROUND((price - LAG(price, 20) OVER (PARTITION BY category ORDER BY num ASC)) / LAG(price, 20) OVER (PARTITION BY category ORDER BY num ASC) * 100, 1) AS `prev20`
FROM data_table dt;

【讨论】:

  • 已在 IF 语句中将 0 更改为 NULL
  • 不客气!您是否出于兴趣尝试了 (category, num) 上的附加键?
  • 只是想知道复合索引在多大程度上提高了原始查询的性能。通过好奇你可以学到很多东西,这真是太神奇了。
  • 它是什么尺寸的?您的意思是要将 perc 列添加到现有表中,然后更新其值?
  • 我已将更新声明添加到我的答案中。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-09-07
  • 2013-03-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-09-26
  • 2015-01-09
相关资源
最近更新 更多