2014-10-31 32 views
0

在我的J2EE Web应用程序中,我需要生成一个条形图,表示系统中具有特定alertsusers的百分比。 (编辑 - 我忘了提及,图表只处理与每个用户的第一个situation相关的警报,因此min(date))。大量sum()的SQL性能

简化我的数据库架构的(但结构上类似的)版本如下:

users { id, name } 
situations { id, user_id, date } 
alerts { id, situation_id, alertA, alertB } 

users哪里到situations是1-n,以及situationsalerts是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。我也在考虑在警报表更新时使用触发器刷新图表特定的表格,但我想这是另一个查询的主题(如果事实证明有问题)。

回答

2

首先,再次考虑你的模式。您将有很多不同的警报,并且您可能不希望为每个警报添加一个列。

考虑更改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 

从我头上的书面未经测试的所有查询。即使他们不像这样工作,你仍然应该明白!

+0

我对此表示歉意,我忘了提及我只对与第一个“情况”相关的'alerts' - 因此'min(date)'感兴趣。我已经相应修改了我原来的问题。 – 2014-11-01 10:20:20

+0

我实际上已经设想将警报重构为每行一个警报模型。在Web应用程序方面,这意味着我的'Alerts'类只包含一个Alert项目列表,我需要做一些重新布线。我的isAlertA()方法只会返回一个contains()或其他的东西 - 没有特别的问题。在我的真实世界的应用程序中,“情境”也包含许多计算出来的指标。你认为这些会从类似的待遇中受益吗?非常感谢 – 2014-11-01 10:31:54

+0

我更新了关于'第一种情况'问题的答案。我希望这有助于! – wolfgangwalther 2014-11-01 13:38:45