如何计算汇总查询中前一周的百分比?

时间:2019-01-19 07:23:32

标签: mysql sql

我面临着一个挑战,最初看来是微不足道的。对于我的开发者大脑而言并非如此。

请考虑以下简单视图,该视图用于验证每个星期六要查询20万条语句的子集的cron。

它如下:

mysql> SELECT
    ->     DATE_FORMAT(s.created, "%Y-%m-%d") as "Date",
    ->     count(s.id) AS "Accounts credited",
    ->     sum(s.withdrawal) "Total Credited"
    ->   --  100 * (sum(s.withdrawal) - sum(prev.withdrawal)) 
         --   / sum(prev.withdrawal) "Difference in %"
    ->     FROM statements s
    -> --    LEFT JOIN prev
    -> --        s.created - interval 7 DAY
    ->  --     ON prev.created = s.created - interval 7 DAY 
        --     AND (prev.status_id = 'OPEN' 
        --     OR prev.status_id = 'PENDING')
    ->     WHERE (s.status_id = 'OPEN' OR  s.status_id = 'PENDING')
    ->     GROUP BY YEAR(s.created), MONTH(s.created), DAY(s.created)
    ->       ORDER BY s.created DESC
    ->     LIMIT 8;

+------------+-------------------+----------------+
| Date       | Accounts credited | Total Credited |
+------------+-------------------+----------------+
| 2019-01-19 |             18175 |        3173.68 |
| 2019-01-12 |             18135 |        4768.43 |
| 2019-01-05 |             17588 |        6968.49 |
| 2018-12-29 |             17893 |        5404.18 |
| 2018-12-22 |             17353 |        7048.18 |
| 2018-12-15 |             16893 |        7181.34 |
| 2018-12-08 |             16220 |        9547.09 |
| 2018-12-01 |             15476 |        7699.59 |
+------------+-------------------+----------------+
8 rows in set (0.79 sec)

按原样,该查询高效而实用。我只想在前一周的总数中添加一列difference in percentage,以-注释掉的代码即可看到。

我尝试了各种方法,但是由于GROUP BY的原因,添加一个内联列以获取上周的sum(withdrawal)会使查询永远运行。

然后我尝试了LEFT JOIN方法,但是显然存在相同的问题。我认为添加的JOIN必须为外部选择的每一行获取上周的总和。

然后我有了查询我的视图的想法(不是很聪明),即使那样,看来我也会遇到同样的问题。

我认为对于这个简单的任务,还有更多的最佳方法。

是否有一种优雅的方法可以从这样的查询中计算出百分比?

存储过程或其他“非普通SQL”方法是否会更优化?

4 个答案:

答案 0 :(得分:1)

我在SQL Server中使用了此查询:

SELECT TOP 8
    DATE_FORMAT(s.created, "%Y-%m-%d") as "Date",
    count(s.id) AS "Accounts credited",
    sum(s.withdrawal) "Total Credited",
    100 * (sum(s.withdrawal) - sum(s1.withdrawal)) / sum(s1.withdrawal) "Difference in %"
FROM statements s
    LEFT JOIN statements s1 ON s1.created = s.created - 7
    AND (s1.status_id = 'OPEN' OR s1.status_id = 'PENDING')
WHERE (s.status_id = 'OPEN' OR  s.status_id = 'PENDING')
GROUP BY YEAR(s.created), MONTH(s.created), DAY(s.created)
ORDER BY s.created DESC
  

您只需处理null或零个s1.withdrawal。

我希望它能工作。

答案 1 :(得分:1)

如果您对原始查询感到满意,那么您就需要一个相关的子查询

select t.*,
         (select totalcredited from t t1 where t1.dt < t.dt order by t1.dt desc limit 1) prev,
         (
         totalcredited / (select totalcredited from t t1 where t1.dt < t.dt order by t1.dt desc limit 1)  * 100
         ) -100 as chg
from  (your query) as t;

答案 2 :(得分:1)

在上一个示例中我注意到一个错误,因此这里有一个更新。 注意:该查询将当前星期与前一周进行比较。 我希望这就是您所需要的。

SELECT
    Date,
    SUM(CASE week WHEN 0 THEN accounts_credited ELSE 0 END) AS 'Accounts credited',
    SUM(CASE week WHEN 0 THEN total_credited ELSE 0 END) AS 'Total Credited',
    100 * (
        SUM(CASE week WHEN 0 THEN total_credited ELSE 0 END) - SUM(CASE week WHEN 1 THEN total_credited ELSE 0 END)
    ) / SUM(CASE week WHEN 1 THEN total_credited ELSE 0 END) AS 'Difference in %'
FROM
(SELECT
    DATE_FORMAT(created, '%Y-%m-%d') as 'Date',
    COUNT(id) AS 'accounts_credited',
    SUM(withdrawal) 'total_credited',
    0 AS 'week'
FROM 
    statements
WHERE 
    status_id IN ('OPEN','PENDING')
AND
    YEARWEEK(created, 1) = YEARWEEK(CURDATE(), 1)
GROUP BY 
    DATE(created)
UNION
SELECT
    DATE_FORMAT(created, '%Y-%m-%d') as 'Date',
    COUNT(id) AS 'accounts_credited',
    SUM(withdrawal) 'total_credited',
    1 AS 'week'
FROM 
    statements
WHERE 
    status_id IN ('OPEN','PENDING')
AND
    (
    DATE(created) >= CURDATE() - INTERVAL DAYOFWEEK(CURDATE())+6 DAY
    AND 
    DATE(created) < CURDATE() - INTERVAL DAYOFWEEK(CURDATE())-1 DAY
    )
GROUP BY 
    DATE(created)
) AS tmp
ORDER BY Date
GROUP BY Date

答案 3 :(得分:1)

这是您的查询:

select date_format(s.created, '%Y-%m-%d') as "Date",
       count(*) AS "Accounts credited",
       sum(s.withdrawal) "Total Credited"
from statements s
where s.status_id in ('OPEN', 'PENDING')
group by date_format(s.created, '%Y-%m-%d')
order by s.created desc
limit 8;

在MySQL中,也许最简单的解决方案是变量。但是,由于围绕MySQL变量的规则,这有点复杂:

select s.*,
       (case when (@new_prev := @prev) = NULL then NULL  -- never gets here
             when (@prev := Total_Credited) = NULL then NULL -- never gets here
             else @new_prev
        end) as previous_week_Total_Credited
from (select date_format(s.created, '%Y-%m-%d') as "Date",
             count(*) AS Accounts_credited,
             sum(s.withdrawal) as Total_Credited
      from statements s
      where s.status_id in ('OPEN', 'PENDING')
      group by date_format(s.created, '%Y-%m-%d')
      order by "Date" desc
     ) s cross join
     (select @prev := NULL) params
 limit 8;

然后,您可以将其用作最终计算的子查询。