【问题标题】:SQL performance of a large number of sum()s大量 sum() 的 SQL 性能
【发布时间】: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 }

其中userssituations 是 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。我还考虑在更新警报表时使用触发器来刷新特定于图表的表,但我想这是不同查询的主题(如果结果有问题)。

【问题讨论】:

    标签: mysql sql performance optimization


    【解决方案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 应用程序方面,这意味着我的 Alertsclass 将只包含一个警报项目列表,我需要进行一些重新布线。我的 isAlertA() 方法只会返回 contains() 或类似的东西 - 不是特别成问题。在我的实际应用程序中,Situations 还包含许多计算指标。你认为这些会从类似的治疗中受益吗?非常感谢
    • 我更新了关于“第一种情况”问题的答案。我希望这会有所帮助!
    • 关于situations 的计算指标。这取决于。如果这些是有限数量的不同计算,我会将它们作为列直接放入situations。相反,如果这些指标都以相同的方式计算,但即仅针对不同的数据子集,或者未来指标的数量会更大(或已经很大),那么我将采用额外表格方法,就像与警报。
    • 创建存储过程或视图将降低 J2EE Web 应用程序中数据库之外的复杂性。您只需要调用该过程或从视图中进行选择,这将是一个单行。您也可以稍后修改您的查询,而无需接触您的 J2EE 源代码,因为它完全存储在 mysql 服务器上。阅读有关何时使用视图的更多信息:stackoverflow.com/questions/1483580/…dba.stackexchange.com/questions/16372/…
    猜你喜欢
    • 2011-03-10
    • 1970-01-01
    • 2021-11-03
    • 2015-08-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-02-24
    • 1970-01-01
    相关资源
    最近更新 更多