1

我正在使用 Amazon S3 为播客提供 MP3 文件。Amazon 提供访问日志,我将其获取并存储在 MySQL 数据库中。数据库如下所示:

request_id  varchar(16)
time        int(10)     
file        varchar(255)        
sent        bigint(20)      
size        bigint(20)      
status      smallint(5)     
ip          varchar(39)         
referrer    varchar(255)        
user_agent  varchar(255)

因为这是一个播客,大量的点击是 206 个范围请求(来自 iTunes/iOS),以小块的形式请求每个文件。

我也有一些人不止一次下载该文件。

所以我想做的是构建一个查询:

  • 每个文件、每个 IP 仅提供一个结果(因此每个 IP/人只能算作每个文件的一次“命中”)。
  • 将每个文件/IP发送的所有字节(列)的总和相加,sent因此我可以看到该特定文件/IP 组合的所有范围请求的总数是多少。
  • 奖励:比较sent每个文件/IP 列的总和,并且仅在字节总和 >=size列的 75% 时返回该结果(目前我正在使用 PHP 进行此比较后查询)。

这是我目前正在使用的,我相信它正在工作,但它的完整后果对GROUP BY我来说是一个谜(例如 - 列顺序GROUP BY是否重要?):

SELECT FROM_UNIXTIME(time,'%M %D') as date, ip, file, SUM(sent) as sent, size
FROM stats
WHERE sent > 0 
AND size > 0
AND FROM_UNIXTIME(time, '%Y-%m-%d') >= '2012-09-01'
AND FROM_UNIXTIME(time, '%Y-%m-%d') <= '2012-09-30'
GROUP BY ip, file
ORDER BY time ASC, file ASC

有人在这里看到任何潜在的陷阱吗?

4

2 回答 2

3

首先回答你的问题:

“分组依据”中的列顺序与结果中返回的内容无关。

您将始终获得相同的分组和相同的聚合值。但是如果列上有索引并且group by中的顺序与索引中列的顺序不匹配,它可能会影响mysql中的性能。我曾经看过一篇讨论性能效果的文章。如果我找到它,我会发布它的链接。

至于您的查询,请注意您选择的字段不属于 group by。这不是 SQL 的标准,但 MySql 允许这样做(可以告诉 MySql 禁止它)。您需要知道的是引擎将创建组,并且对于这些字段,它将从在组中遇到的第一行中选择第一个值。根据您的查询方式,这不能保证正确的结果。原因是 order by 在完成后应用于结果。可能的解决方案很少,但我会提到使用子查询并在内部设置顺序以确保在分组发生之前第一行是您需要的行。

SELECT in_tab.date, in_tab.ip, in_tab.file, SUM(in_tab.sent), in_tab.size
FROM (
  SELECT FROM_UNIXTIME(time,'%M %D') as date, ip, file, sent, size
  FROM stats
  WHERE sent > 0 
  AND size > 0
  AND FROM_UNIXTIME(time, '%Y-%m-%d') >= '2012-09-01'
  AND FROM_UNIXTIME(time, '%Y-%m-%d') <= '2012-09-30'
  ORDER BY time ASC
) in_tab
GROUP BY in_tab.ip, in_tab.file
ORDER BY in_tab.date ASC, in_tab.file ASC

如您所见,子查询中的顺序是在分组开始之前执行的。因此,在外部查询创建的每个组中,字段日期和大小将从第一行获取,即具有最早日期的字段(如果您想要最近的,请在子查询中更改为 DESC)。外部 order by 用于对最终结果进行排序,您可以根据需要更改它。

现在对于 BONUS:为了只得到总和 >= 75% 大小的结果,您可以使用 HAVING 子句:

SELECT in_tab.date, in_tab.ip, in_tab.file, SUM(in_tab.sent) as total_sent, in_tab.size
FROM (
  SELECT FROM_UNIXTIME(time,'%M %D') as date, ip, file, sent, size
  FROM stats
  WHERE sent > 0 
  AND size > 0
  AND FROM_UNIXTIME(time, '%Y-%m-%d') >= '2012-09-01'
  AND FROM_UNIXTIME(time, '%Y-%m-%d') <= '2012-09-30'
  ORDER BY time ASC
) in_tab
GROUP BY in_tab.ip, in_tab.file
HAVING total_sent > = 0.75 * in_tab.size
ORDER BY in_tab.date ASC, in_tab.file ASC

我建议您考虑最佳解决方案:切换到标准 sql。从长远来看,它更好,更安全。然后,您将在一个查询中对所需数据进行分组,并在另一个查询(或使用联接)中获取其他信息(如文件大小和最小/最大日期)。答案已经很长了,我将不得不停止解释和举例说明。

于 2012-10-18T01:15:06.597 回答
0

按列顺序分组确实很重要,它按第一列分组,然后按下一个分组,因此按 A 分组,B 会将结果分组为 A,然后按 B 分组,如果 B 是相似的数据,这很重要。

于 2012-10-18T00:03:08.817 回答