大量 sum() 的 SQL 性能
Posted
技术标签:
【中文标题】大量 sum() 的 SQL 性能【英文标题】:SQL performance of a large number of sum()s 【发布时间】:2014-10-31 23:42:03 【问题描述】:在我的 J2EE Web 应用程序中,我需要生成一个条形图,表示系统中users
与特定alerts
的百分比。 (编辑 - 我忘了提,该图只处理与每个用户的第一个 situation
相关联的警报,因此是 min(date)
)。
我的数据库架构的简化(但结构相似)版本如下:
users id, name
situations id, user_id, date
alerts id, situation_id, alertA, alertB
其中users
到 situations
是 1-n,situations
到 alerts
是 1-1。
我省略了数据类型,但警报(alertA 和 B)是布尔值。在我的实际案例中,有很多这样的警报(30-ish)。
到目前为止,这是我想出的:
select sum(alerts.alertA), sum(alerts.alertB)
form alerts, (
select id, min(date)
from situations
group by user_id) as situations
where situations.id = alerts.situation_id;
然后将这些总和除以
select count(users.id) from users;
这似乎远非理想。
您对如何改进查询的建议/建议将不胜感激(或者我可能需要重新考虑我的数据库架构)...
谢谢,
安东尼
PS。我还考虑在更新警报表时使用触发器来刷新特定于图表的表,但我想这是不同查询的主题(如果结果有问题)。
【问题讨论】:
【参考方案1】:首先,再次考虑您的架构。您将有很多不同的警报,并且您可能不想为每个警报添加一个列。
考虑将alerts
表更改为 id, situation_id, type, value
,其中type
将是(A,B,C,....)
,value
将是您的布尔值。
然后您计算百分比的任务将分为:
(1)统计用户总数:
SELECT COUNT(id) AS total FROM users
(2) 找到每个用户的“第一个”情况:
SELECT situations.id, situations.user_id
-- selects the minimum date for every user_id
FROM (SELECT user_id, MIN(date) AS min_date
FROM situations
GROUP BY user_id) AS first_situation
-- gets the situations.id for user with minimum date
JOIN situations ON
first_situation.user_id = situations.user_id AND
first_situation.min_date = situations.date
-- limits number of situations per user to 1 (possible min_date duplicates)
GROUP BY user_id
(3) 统计在子查询中的至少一种情况下设置警报的用户数:
SELECT
alerts.type,
COUNT(situations.user_id)
FROM ( ... situations.user_id, situations.id ... ) AS situations
JOIN alerts ON
situations.id = alerts.situation_id
WHERE
alerts.value = 1
GROUP BY
alerts.type
将这三个步骤放在一起,得到如下结果:
SELECT
alerts.type,
COUNT(situations.user_id)/users.total
FROM (SELECT situations.id, situations.user_id
FROM (SELECT user_id, MIN(date) AS min_date
FROM situations
GROUP BY user_id) AS first_situation
JOIN situations ON
first_situation.user_id = situations.user_id AND
first_situation.min_date = situations.date
GROUP BY user_id
) AS situations
JOIN alerts ON
situations.id = alerts.situation_id
JOIN (SELECT COUNT(id) AS total FROM users) AS users
WHERE
alerts.value = 1
GROUP BY
alerts.type
所有查询都是从我的脑海中写出来的,没有经过测试。即使它们的工作方式不完全一样,您仍然应该明白这一点!
【讨论】:
抱歉,我忘了提到我只对与第一个situation
相关联的 alerts
感兴趣——因此是 min(date)
。我已经相应地修改了我原来的问题。
我实际上已经考虑将警报重组为每行一个警报模型。在 Web 应用程序方面,这意味着我的 Alerts
class 将只包含一个警报项目列表,我需要进行一些重新布线。我的 isAlertA() 方法只会返回 contains() 或类似的东西 - 不是特别成问题。在我的实际应用程序中,Situations
还包含许多计算指标。你认为这些会从类似的治疗中受益吗?非常感谢
我更新了关于“第一种情况”问题的答案。我希望这会有所帮助!
关于situations
的计算指标。这取决于。如果这些是有限数量的不同计算,我会将它们作为列直接放入situations
。相反,如果这些指标都以相同的方式计算,但即仅针对不同的数据子集,或者未来指标的数量会更大(或已经很大),那么我将采用额外表格方法,就像与警报。
创建存储过程或视图将降低 J2EE Web 应用程序中数据库之外的复杂性。您只需要调用该过程或从视图中进行选择,这将是一个单行。您也可以稍后修改您的查询,而无需接触您的 J2EE 源代码,因为它完全存储在 mysql 服务器上。阅读有关何时使用视图的更多信息:***.com/questions/1483580/…dba.stackexchange.com/questions/16372/…以上是关于大量 sum() 的 SQL 性能的主要内容,如果未能解决你的问题,请参考以下文章