SQL性能的大量sum()s

时间:2014-10-31 23:42:03

标签: mysql sql performance optimization

在我的J2EE Web应用程序中,我需要生成一个条形图,表示具有特定users的系统中alerts的百分比。 (编辑 - 我忘了提及,该图表仅处理与每个用户的第一个situation相关联的提醒,因此min(date))。

我的数据库模式的简化(但结构相似)版本如下:

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

其中userssituations为1-n,situationsalerts为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 个答案:

答案 0 :(得分: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

所有查询都是从我的脑子里写的,没有经过测试。即使他们不能完全像那样工作,你仍然应该得到这个想法!

相关问题