Mysql 如何优化此DB查询以计算收到的SMS消息的每日总数

Mysql 如何优化此DB查询以计算收到的SMS消息的每日总数,mysql,sql,database,Mysql,Sql,Database,为了简单地解释我的问题,SMS网关使用GET请求连接到我的PHP脚本,消息内容被提取并保存到MySQL数据库中。消息内容包括一个时间戳值,该值保存在同一个表中。只使用了一个表,列为ID(int)、sender(int)、message(varchar)、timestamp(timestamp) SMS网关接收来自大约100个不同号码的消息,每天从每个号码接收的最大消息数为400条,尽管它们不每天发送消息。在一个月内,该脚本可能会保存多达300000条消息 我必须创建一个基于浏览器的报告,该报告生

为了简单地解释我的问题,SMS网关使用GET请求连接到我的PHP脚本,消息内容被提取并保存到MySQL数据库中。消息内容包括一个时间戳值,该值保存在同一个表中。只使用了一个表,列为ID(int)、sender(int)、message(varchar)、timestamp(timestamp)

SMS网关接收来自大约100个不同号码的消息,每天从每个号码接收的最大消息数为400条,尽管它们不每天发送消息。在一个月内,该脚本可能会保存多达300000条消息

我必须创建一个基于浏览器的报告,该报告生成一个表,其中包含每个数字的一行,然后是从该数字接收的邮件数量的每日总计,因此表中的第一列显示该数字,第二列显示8月1日接收的邮件总数,第三列显示8月2日收到的邮件总数等

脚本本身工作正常,但由于需要检索的总数而超时。我通过以下两种方式实现了查询,但都没有在60秒的最长脚本执行时间内完成:

  • 检索数据库中不同的数字列表,然后循环遍历数字列表,并使用COUNT()语句对每个数字运行多达31个查询,以检索每日总数

  • 在数据库中检索一个不同的数字列表,然后在数字列表中循环,并运行一个查询来检索当月收到的每条消息的时间戳值。一旦检索到时间戳值列表,结果将循环,提取时间戳的日期部分并更新数组-数组键是当月的日期,值是当月收到的消息总数。当时间戳值与日期匹配时,数组值将递增一

  • 这两种解决方案似乎都不理想,因为第一种解决方案必须运行太多单独的查询,而第二种解决方案则检索大量也必须处理的数据

    我在这个阶段考虑的方法是要么考虑添加某种GROUPBY子句,要么只提取时间戳的日期部分,将其保存到一个单独的列中,然后在已经索引的number列之外索引该列


    感谢您提供的任何建议或帮助。我在优化数据库方面的选择是有限的,因为我不能更改数据库类型,也不能编辑MySQL配置文件,所以我必须确保我的查询设计正确,数据库模式是最佳的。

    一般来说,通过单个查询检索所需的结果更有效

    就原始MySQL性能而言,使用以下查询可能会更好:

    SELECT t.number
         , DATE(t.message_datetime) AS dt
         , COUNT(1)                 AS cnt
      FROM sms_messages t
     WHERE t.message_datetime >= '2014-07-01'
       AND t.message_datetime <  '2014-07-01' + INTERVAL 1 MONTH
     GROUP
        BY t.number
         , DATE(t.message_datetime)
    

    非常感谢。我认为如果一个月内根本没有收到任何邮件的号码没有收到,那就不会有问题。我现在的方法是只为日期创建一个单独的列,看看这是否会提高性能,然后按照您的建议重写查询。如果问题难以解决,我还将研究在单独的表中缓存每日总计,并使用cron作业脚本填充该表。我认为这将是唯一可行的解决方案,无论如何,如果有100万或更多的邮件需要查询。再次感谢您的帮助-第二次查询做了我需要的。我的解决方案是使用两个查询来检索结果——第一个查询检索不同数字的列表,循环搜索结果,并使用数字作为数组键来创建二维数组,使数组看起来类似于此数组('557777123456'=>数组('2014-08-01'=>'0','2014-08-02'=>'0'))。使用第二个查询,我可以像这样填充这个数组$num_数组[$number][$date]=$cnt;使用该方法,每个数字都有所有日期的条目,默认值始终为0。@NoelWhitemore:就MySQL而言,这是最有效的。您正在提取尽可能小的结果集以满足需求。(对于第一个查询,如果您没有一个只包含不同的
    number
    的单独表,则最适合使用前导列为
    number
    的索引)
    SELECT t.number
         , t.dt
         , COUNT(1) AS cnt
      FROM sms_messages t
     WHERE t.message_datetime >= '2014-07-01'
       AND t.message_datetime <  '2014-07-01' + INTERVAL 1 MONTH
     GROUP
        BY t.number
         , t.dt
    
    ... ON sms_messages (number, dt)