问题描述
假设你有一个这样的表:
表用户:
如果我这样写查询:
SELECT DATE_FORMAT(regdate,"%Y-%m-01") as regmonth,idprovince,count(userid) as num FROM users GROUP BY DATE_FORMAT(regdate,"%Y-%m-01"),idprovince
这将正确生成分组结果,显示在任何给定月份和省份注册的新用户数量。
假设现在我想要任何给定月份每个省的累计用户数(任何给定月份和省份的值应该是该月份和省份的新用户以及该月份之前所有月份的总和省),我应该如何构建高效的查询?
我试过使用这样的子查询:
9
它工作正常,但需要 AGES 才能运行,比如在 70k 行的表上运行 70 多秒。
知道如何提高效率吗?
我使用的是 MysqL 5.5,但如果有用我可以升级到 MysqL 8。
感谢您的帮助!
解决方法
在 mysql 5.5 中,您使用用户定义的变量来总结不同行的数字。
你必须保持列的顺序,否则算法将不起作用
CREATE tABLE users (userid int,regdate date,idprovince int )
INSERT INTO users VALUEs (1,'2020-01-21',1),(2,'2020-02-21',(3,'2020-03-21',(4,2),(5,(6,2)
SELECT
regmonth,IF(@idprovince = idprovince,@num:=@num + `num`,@num:= `num`) as num,@idprovince := idprovince as idprovince
FROM
(SELECT
DATE_FORMAT(regdate,'%Y-%m-01') AS regmonth,idprovince,COUNT(userid) AS num
FROM
users
GROUP BY DATE_FORMAT(regdate,'%Y-%m-01'),idprovince
ORDER BY idprovince,DATE_FORMAT(regdate,'%Y-%m-01')) t1,(SELECT @num:=0,@idprovince := 0) t2
regmonth | num | idprovince :--------- | --: | ---------: 2020-01-01 | 1 | 1 2020-02-01 | 2 | 1 2020-03-01 | 3 | 1 2020-01-01 | 1 | 2 2020-02-01 | 2 | 2 2020-03-01 | 3 | 2
dbfiddle here
,感谢@nbk 的输入,我设法创建了这个查询,它既快速又正确,并且基于每个月必须至少有一个用户注册的唯一假设;如果不是这样,则应研究另一种生成月份列表的方法。
SELECT regmonth,num,cumnum FROM (SELECT regmonth,@cumnum:=@cumnum + `num`,@cumnum:= `num`) as cumnum,@idprovince := idprovince as idprovince,num FROM ( select users2.regmonth,users3.idprovince,coalesce(num,0) as num FROM (select date_format(regdate,"%Y-%m-01") as regmonth from users group by date_format(regdate,"%Y-%m-01") ) as users2 CROSS JOIN provinces (select idprovince from users group by idprovince ) as users3 LEFT JOIN (SELECT idprovince,DATE_FORMAT(users.regdate,"%Y-%m-01") as regmonth,count(id) as num from users GROUP BY idprovince,"%Y-%m-01") ) as users_totals on users_totals.idprovince=users3.idprovince AND user_totals.regmonth=users2.regmonth order by users3.idprovince,regmonth ) as t1,(SELECT @cumnum:=0,@idprovince := 0 ) as t2 ) as t3 ORDER BY regmonth,idprovince
事实上,整个查询基于在用户表中作为regdate存在的所有月份和用户表中存在的所有省份ID之间的CROSS JOIN(笛卡尔积)开始。这可确保表示具有现有省份 ID 的月份的所有组合。
然后我们计算每个组中的正常计数,并将其连接到笛卡尔积,当连接失败时向零添加合并。
然后使用@nbk 提出的方法生成运行总计,最后放置一个外部查询来恢复典型的基于时间的排序(已更改为正确求和累计总数)。
这终于奏效了! :)
,建立并维护每日小计的“汇总表”。每天晚上更新它,只添加前一天的新数据。然后,要获得“报告”,请总结该汇总表中的总和。更多讨论:http://mysql.rjweb.org/doc.php/summarytables