MySQL - 分组和计数 - 最佳查询

时间:2014-06-30 15:32:38

标签: mysql group-by

我们有一个统计数据库,我们希望将一些结果分组。每个条目都有一个时间戳'tstarted'。

我们希望在每天的每个季度进行分组。对于每个季度,我们想知道我们所拥有的日期数> 0结果(该季度)。

我们可以使用子查询来解决这个问题:

select quarter, sum(q), count(quarter), sum(q) / count(quarter) as average
from (
    select SEC_TO_TIME((TIME_TO_SEC(tstarted) DIV 900) * 900) as quarter, sum(qdelivered) as q
    from statistics 
    where stat_field = 1
    group by SEC_TO_TIME((TIME_TO_SEC(tstarted) DIV 900) * 900), date(tstarted)
    order by SEC_TO_TIME((TIME_TO_SEC(tstarted) DIV 900) * 900) asc
) as sub
group by quarter

我的问题:是否有更有效的方法来检索此结果(例如加入或其他方式)?

2 个答案:

答案 0 :(得分:2)

通过消除内联视图(派生表别名为sub)并在单个查询中完成所有工作,可以提高效率。 (这是因为MySQL处理内联视图,创建和填充临时MyISAM表的方式。)

<击> 我不明白为什么表达式date(tstarted)需要包含在GROUP BY子句中;我没有看到删除它会改变查询返回的结果集。

我现在看到将date(tstarted)包含在内联视图查询的GROUP BY中的效果。

我认为此查询返回与原始查询相同的结果:

SELECT SEC_TO_TIME((TIME_TO_SEC(s.tstarted) DIV 900) * 900) AS `quarter`
     , SUM(s.qdelivered)                                    AS `q`
     , COUNT(DISTINCT DATE(s.tstarted))                     AS `day_count`
     , SUM(s.qdelivered) / COUNT(DISTINCT DATE(s.tstarted)) AS `average`
  FROM statistics s
 WHERE s.stat_field = 1 
 GROUP BY SEC_TO_TIME((TIME_TO_SEC(s.tstarted) DIV 900) * 900)

这应该更有效,因为它避免了实现中间派生表。


你的问题说你想要一天算一次&#34 ;;这听起来像是你想要计算每天在特定的四分之一小时内排的一天。

要实现这一点,您只需将聚合表达式添加到SELECT列表

即可
     , COUNT(DISTINCT DATE(s.tstarted))                     AS `day_count`

答案 1 :(得分:1)

我很想在当天设置一个宿舍表。使用此表并LEFT JOIN您的统计表。

CREATE TABLE quarters
(
    id  INT,
    start_qtr   INT,
    end_qtr INT
);

INSERT INTO quarters (id, start_qtr, end_qtr) VALUES
(1,0,899),
(2,900,1799),
(3,1800,2699),
(4,2700,3599),
(5,3600,4499),
(6,4500,5399),
(7,5400,6299),
(8,6300,7199),
etc;

您的查询可以是: -

SELECT SEC_TO_TIME(quarters.start_qtr) AS quarter, 
        sum(statistics.qdelivered), 
        count(statistics.qdelivered), 
        sum(statistics.qdelivered) / count(statistics.qdelivered) as average
FROM quarters
LEFT OUTER JOIN statistics
ON TIME_TO_SEC(statistics.tstarted) BETWEEN quarters.start_qtr AND quarters.end_qtr
AND statistics.stat_field = 1
AND DATE(statistics.tstarted) = '2014-06-30'
GROUP BY quarter
ORDER BY quarter;

这样做的好处是,对于没有统计数据的季度,它将为您提供计数为0(平均值为NULL)的条目,并保存一些计算。

您可以通过向季度表添加时间列来保存更多计算: -

CREATE TABLE quarters
(
    id  INT,
    start_qtr   INT,
    end_qtr INT
    start_qtr_time  TIME,
    end_qtr_time    TIME,
);

INSERT INTO quarters (id, start_qtr, end_qtr, start_qtr_time, end_qtr_time) VALUES
(1,0,899, '00:00:00', '00:14:59'),
(2,900,1799, '00:15:00', '00:29:59'),
(3,1800,2699, '00:30:00', '00:44:59'),
(4,2700,3599, '00:45:00', '00:59:59'),
(5,3600,4499, '01:00:00', '01:14:59'),
(6,4500,5399, '01:15:00', '01:29:59'),
(7,5400,6299, '01:30:00', '01:44:59'),
(8,6300,7199, '01:45:00', '01:59:59'),
etc

然后这样可以在JOIN上保存函数的使用: -

SELECT start_qtr_time AS quarter, 
        sum(statistics.qdelivered), 
        count(statistics.qdelivered), 
        sum(statistics.qdelivered) / count(statistics.qdelivered) as average
FROM quarters
LEFT OUTER JOIN statistics
ON TIME(statistics.tstarted) BETWEEN quarters.start_qtr_time AND quarters.end_qtr_time
AND statistics.stat_field = 1
AND DATE(statistics.tstarted) = '2014-06-30'
GROUP BY quarter
ORDER BY quarter;

这两者都假设您对某一天感兴趣。