MySQL 5.6 中按类别计算滚动百分比变化
Calculating rolling percentage change by category in MySQL 5.6
我有一个这样的 SQL 数据 table,我想计算滚动百分比变化(按行和类别)。所以结果如下所示
我使用的 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;
您可以使用用户变量 -
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 值更新您的 table,您可以使用 -
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;
我有一个这样的 SQL 数据 table,我想计算滚动百分比变化(按行和类别)。所以结果如下所示
我使用的 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;
您可以使用用户变量 -
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 值更新您的 table,您可以使用 -
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;