代码之家  ›  专栏  ›  技术社区  ›  merkuro

如何最小化需要使用不同Inverval分组的查询中的负载?

  •  0
  • merkuro  · 技术社区  · 17 年前

    我正在寻找一个最佳实践建议,如何加快查询速度,同时最小化调用date/mktime函数所需的开销。为了简化问题,我正在处理以下表格布局:

    CREATE TABLE my_table(
      id INTEGER PRIMARY KEY NOT NULL AUTO_INCREMENT,   
      important_data INTEGER,
      date INTEGER);
    

    用户可以选择显示1)两个日期之间的所有条目:

    SELECT * FROM my_table 
      WHERE date >= ? AND date <= ? 
      ORDER BY date DESC;
    

    输出:

    10-21-2009 12:12:12, 10002
    10-21-2009 14:12:12, 15002
    10-22-2009 14:05:01, 20030
    10-23-2009 15:23:35, 300
    ....  
    

    2) 按天、周、月、年对输出进行汇总/分组:

    SELECT COUNT(*) AS count, SUM(important_data) AS important_data
      FROM my_table 
      WHERE date >= ? AND date <= ? 
      ORDER BY date DESC;
    

    按月输出的示例:

    10-2009, 100002
    11-2009, 200030
    12-2009, 3000
    01-2010, 0 /* <- very important to show empty dates, with no entries in the table! */
    ....  
    

    为了完成选项2),我目前正在使用mktime/date运行一个非常昂贵的for循环,如下所示:

    for(...){ /* example for group by day */
      $span_from = (int)mktime(0, 0, 0, date("m", $time_min), date("d", $time_min)+$i, date("Y", $time_min));
      $span_to = (int)mktime(0, 0, 0, date("m", $time_min), date("d", $time_min)+$i+1, date("Y", $time_min)); 
      $query = "..";  
      $output = date("m-d-y", ..);
    }
    

    到目前为止,我的想法是什么?为日(20091212)、月(200912)、周(200942)和年(2009)添加额外/冗余列(整数)。这样我就可以消除for循环中所有不必要的查询。然而,我仍然面临着一个问题,就是要非常快速地计算数据库中没有任何等价物的所有日期。解决这个问题的一种方法是让MySQL来完成这项工作,只需使用一个大查询(计算所有日期/使用MySQL日期函数)和一个左连接(数据)。让MySQL承担额外的负载是否明智?无论如何,我不愿意在for循环中使用所有这些mktime/date。因为我可以完全控制表格的布局和代码,所以即使是有重大更改的建议也是受欢迎的!

    使现代化

    多亏了Greg,我提出了以下SQL查询。但是,使用50行sql语句(使用php构建)仍然让我感到困扰,否则可能会更快、更优雅:

    SELECT * FROM (  
      SELECT DATE_ADD('2009-01-30', INTERVAL 0 DAY) AS day UNION ALL
      SELECT DATE_ADD('2009-01-30', INTERVAL 1 DAY) AS day UNION ALL
      SELECT DATE_ADD('2009-01-30', INTERVAL 2 DAY) AS day UNION ALL
      SELECT DATE_ADD('2009-01-30', INTERVAL 3 DAY) AS day UNION ALL
      ......
      SELECT DATE_ADD('2009-01-30', INTERVAL 50 DAY) AS day ) AS dates
    LEFT JOIN (
        SELECT DATE_FORMAT(date, '%Y-%m-%d') AS date, SUM(data) AS data
        FROM test 
        GROUP BY date  
      ) AS results
    ON DATE_FORMAT(dates.day, '%Y-%m-%d') = results.date;
    
    2 回复  |  直到 17 年前
        1
  •  1
  •   Greg    17 年前

    您绝对不应该在循环中执行查询。 您可以这样分组:

    SELECT COUNT(*) AS count, SUM(important_data) AS important_data, DATE_FORMAT('%Y-%m', date) AS month
      FROM my_table 
      WHERE date BETWEEN ? AND ? -- This should be the min and max of the whole range
      GROUP BY  DATE_FORMAT('%Y-%m', date)
      ORDER BY date DESC;
    

    然后将它们拉入一个由日期键控的数组中,并在数据范围内循环(该循环在CPU上应该很轻)。

        2
  •  0
  •   Mercer Traieste    17 年前

    另一个想法是不要在查询中使用字符串。在mysql上,将字符串参数转换为datetime。

    STR_TO_DATE(str,format)
    

    http://dev.mysql.com/doc/refman/5.0/en/date-and-time-functions.html

    推荐文章