MySQL查询以获得总百分比变化

问题描述 投票:0回答:4

如何在MySQL中添加一列百分比变化(而不是percentage points)?

有一个包含百分比变化列的表格:

+---------+
| percent |
+---------+
|   -0.50 |
|    0.50 |
|    1.00 |
|   -0.20 |
|    0.50 |
|   -1.00 |
|   -2.00 |
|    0.75 |
|    1.00 |
|    0.50 |
+---------+

如何编写一个查询来计算每行值的总变化百分比,以便计算出的行表示其百分比变化以及之前所有百分比变化行?

预期结果:

+---------+---------------+---------------+
| percent | nominal_value | total_percent |
+---------+---------------+---------------+
|   -0.50 |          0.50 |         -0.50 |
|    0.50 |          0.75 |         -0.25 |
|    1.00 |          1.50 |          0.50 |
|   -0.20 |          1.20 |          0.20 |
|    0.50 |          1.80 |          0.80 |
|   -1.00 |          0.00 |         -1.00 |
|   -2.00 |         -2.00 |         -3.00 |
|    0.75 |         -0.50 |         -1.50 |
|    1.00 |          0.00 |         -1.00 |
|    0.50 |          0.50 |         -0.50 |
+---------+---------------+---------------+

其中nominal_value是由percent改变的任意值,因此对于第一行如果名义值为1.0(100%)但由-0.50-50%)改变则导致名义值0.5

然后在第二行percent变化是+0.50+50%)所以名义值增加了一半0.5 => 0.75,但也可以说它只是由-0.25-25%)从其原始值降低,因为从1.00.75-0.25-25%1.0

这正是我在更换total_percent之后,nominal_value只是出于解释目的而不需要。

我正在使用MySQL 8,因此查询可能会使用窗口函数/范围等。

这是要复制的测试表:

CREATE TABLE IF NOT EXISTS test
(
    percent DECIMAL(5,2) NOT NULL
)
ENGINE = InnoDB
;

INSERT INTO test (percent) VALUES 
(-0.50)
,(0.50)
,(1.00)
,(-0.20)
,(0.50)
,(-1.0)
,(-2.0)
,(0.75)
,(1.0)
,(0.50)
;
mysql percentage calculation
4个回答
1
投票
DROP TABLE IF EXISTS test;

CREATE TABLE test
( id SERIAL PRIMARY KEY
, percent DECIMAL(5,2) NOT NULL
);

INSERT INTO test (percent) VALUES 
(-0.5)
,(0.5)
,(1)
,(-0.2)
,(0.5)
,(-1)
;

SELECT ROUND(@i:=(@i+(@i*percent)),2)n 
  FROM test
     , (SELECT @i:=1) vars 
 ORDER 
    BY id;
+------+
| n    |
+------+
| 0.50 |
| 0.75 |
| 1.50 |
| 1.20 |
| 1.80 |
| 0.00 |
+------+
6 rows in set (0.00 sec)

mysql>

2
投票

此查询将为您提供所需的结果。它使用两个CTE,第一个简单地向数据添加行号,第二个是递归CTE,它从当前的nominal_value和前面的percent(前面的行号由行号定义)生成nominal_value值。最后total_percent是从nominal_value计算出来的。

注意

为了使这个(和任何类似的)查询可靠地工作,必须有一个PRIMARY KEY,第一个CTE可以按其排序结果。在演示中,我为此添加了AUTO_INCREMENT INTid

WITH RECURSIVE cte AS (
  SELECT percent, ROW_NUMBER() OVER () AS rn
  FROM test
  ORDER BY id),
cte2 AS (
  SELECT 1 + percent AS nominal_value, rn
  FROM cte
  WHERE rn = 1
  UNION ALL
  SELECT CASE WHEN nominal_value = 0 THEN percent
              ELSE nominal_value + percent * ABS(nominal_value)
              END,
         cte.rn
  FROM cte
  JOIN cte2 ON cte2.rn = cte.rn - 1
  )
SELECT percent, nominal_value, (nominal_value - 1) AS total_percent
FROM cte2
JOIN cte ON cte.rn = cte2.rn

输出:

percent nominal_value   total_percent
-0.5    0.5             -0.5
0.5     0.75            -0.25
1       1.5             0.5
-0.2    1.2             0.2
0.5     1.8             0.8
-1      0               -1
-2      -2              -3
0.75    -0.5            -1.5
1       0               -1
0.5     0.5             -0.5

Demo on dbfiddle


1
投票

计算此数据的另一种方法是使用存储过程。这种方法的优点是它不需要递归CTE或变量,但缺点是使用结果可能很棘手(例如在JOIN中)。此过程创建一个临时表以在返回结果之前存储结果;如果需要进一步处理,可以保留该表而不是在程序结束时使用DROPped。与其他答案一样,这种方法要求数据具有PRIMARY KEY以保证一致的结果。

DELIMITER //
CREATE PROCEDURE total_percent()
BEGIN
  DECLARE nominal_value DECIMAL(10,2) DEFAULT 1;
  DECLARE this_percent DECIMAL(5,2);
  DECLARE done INT DEFAULT 0;
  DECLARE p_cursor CURSOR FOR SELECT percent FROM test ORDER BY id;
  DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
  CREATE TEMPORARY TABLE p (percent DECIMAL(5, 2),
                            nominal_value DECIMAL(10, 2),
                            total_percent DECIMAL(10, 2));
  OPEN p_cursor;
  compute: LOOP
    FETCH p_cursor INTO this_percent;
    IF done THEN
      LEAVE compute;
    END IF;
    IF nominal_value = 0 THEN
      SET nominal_value = this_percent;
    ELSE
      SET nominal_value = nominal_value + this_percent * ABS(nominal_value);
    END IF;
    INSERT INTO p VALUES (this_percent, nominal_value, nominal_value -1);
  END loop;
  SELECT * FROM p;
  DROP TABLE p;
END //
DELIMITER ;

CALL total_percent();

输出:

percent  nominal_value   total_percent
-0.5     0.5             -0.5
0.5      0.75            -0.25
1        1.5             0.5
-0.2     1.2             0.2
0.5      1.8             0.8
-1       0               -1
-2       -2              -3
0.75     -0.5            -1.5
1        0               -1
0.5      0.5             -0.5

Demo on dbfiddle


1
投票

这是接受答案的一个小变化,因为OP编辑了帖子并且在接受的ans之后添加了额外的数据行和想要的结果。发布并接受:

查询:

DROP TABLE IF EXISTS test;

CREATE TABLE test
( 
 id SERIAL PRIMARY KEY
 , percent DECIMAL(5,2) NOT NULL
);

INSERT INTO test (percent) VALUES 
(-0.50)
,(0.50)
,(1.00)
,(-0.20)
,(0.50)
,(-1.0)
,(-2.0)
,(0.75)
,(1.0)
,(0.50)
;

SELECT 
    percent,

    CASE @i 
        WHEN 0 THEN ROUND(@i:=(@i+(percent * 1)),2) -1
        ELSE ROUND(@i:=(@i+(percent * ABS(@i))) ,2) -1
    END total_percent

FROM 
    test
    , (SELECT @i:=1) vars         
ORDER 
    BY id; 

结果:

+---------+---------------+
| percent | total_percent |
+---------+---------------+
|   -0.50 |         -0.50 |
|    0.50 |         -0.25 |
|    1.00 |          0.50 |
|   -0.20 |          0.20 |
|    0.50 |          0.80 |
|   -1.00 |         -1.00 |
|   -2.00 |         -3.00 |
|    0.75 |         -1.50 |
|    1.00 |         -1.00 |
|    0.50 |         -0.50 |
+---------+---------------+
10 rows in set, 3 warnings (0.00 sec)

请注意,接受的答案在达到零标称值后停止计算,然后无论百分比变化没有差异,标称值是相同的= 0.对于某些情况,这可能是正确的方法。对于其他人来说,如果您使用MySQL 8,则通过零或@Nick答案继续计算。

© www.soinside.com 2019 - 2024. All rights reserved.