我面临着一个挑战,最初看来是微不足道的。对于我的开发者大脑而言并非如此。
请考虑以下简单视图,该视图用于验证每个星期六要查询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”方法是否会更优化?
答案 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;
然后,您可以将其用作最终计算的子查询。