【问题标题】:MySQL Query - Using Aggregate and Group By to generate separate resultsMySQL Query - 使用 Aggregate 和 Group By 生成单独的结果
【发布时间】:2010-09-20 15:35:58
【问题描述】:

我有一个表,其中一个或多个具有相同“id”值的条目可以插入到我们的日志/事实表中(包含超过 100+ 百万条记录)
以设定的频率将一条新记录插入到此表中,并为“created”和“view_percent”列(观看视频的百分比)设置一个新值。
对于两个不同的查询,我想返回:

预期结果 1:

+------------------+--------------+-------------+------------------+------------+ | archive_asset_id | asset_title | count_asset | avg_view | time_day | +------------------+--------------+-------------+------------------+------------+ | 83386 | Oliver James | 4 | 36.75 | 2010-08-09 | +------------------+--------------+-------------+------------------+------------+

使用此查询,我无法在执行聚合函数时过滤掉必要的记录...取而代之的是所有行的平均值,结果值为 31.307

SELECT archive_asset_id, asset_title, COUNT(DISTINCT id * 1000000 + archive_asset_id) AS count_asset, AVG(view_percent) AS avg_view, FROM_UNIXTIME(created, '%Y-%m-%d') AS time_day
FROM log_embed_video 
WHERE archive_asset_id = 83386 
AND created >= 1281312000
AND created < 1281484800
GROUP BY time_day
ORDER BY time_day;

预期结果 2:

+------------+------------------+---------------+------------------+-------------------------------+ | time_day | archive_asset_id | asset_title | MAX(view_percent) | occurrences MAX(view_percent) | +------------+------------------+---------------+------------------+-------------------------------+ | 2010-08-09 | 83386 | Oliver James | 13 | 1 | | 2010-08-09 | 83386 | Oliver James | 17 | 2 | | 2010-08-09 | 83386 | Oliver James | 100 | 1 | +------------+------------------+---------------+-------------------+------------------------------+

这是我用于结果 2 的查询,但不是我想要的... log_embed_video.id 的组产生 4 个结果... 这是给定查询的预期结果,但不是预期的输出。

SELECT id, FROM_UNIXTIME(created, '%Y-%m-%d') AS time_day, archive_asset_id, asset_title, COUNT(DISTINCT id * 1000000 + archive_asset_id) AS 'count_asset', MAX(view_percent) as 'max_view_percent'
FROM log_embed_video 
WHERE archive_asset_id = 83386 
AND created >= 1281312000
AND created < 1281484800
GROUP BY time_day, id



条件数据:
带有 KEEP 标记的行是我在返回结果 1 和结果 2 时要使用的数据。

SELECT id, archive_asset_id, asset_title, view_percent, FROM_UNIXTIME(created, '%Y-%m-%d') AS time_day
FROM log_embed_video 
WHERE archive_asset_id = 83386 
AND created >= 1281312000
AND created < 1281484800
ORDER BY id, view_percent;
+----------+------------------+--------------+--------------+------------+ | id | archive_asset_id | asset_title | view_percent | time_day | +----------+------------------+--------------+--------------+------------+ | 43326898 | 83386 | Oliver James | 0 | 2010-08-09 | - DISCARD RECORD / DUPLICATE | 43326898 | 83386 | Oliver James | 13 | 2010-08-09 | + KEEP | 43432090 | 83386 | Oliver James | 0 | 2010-08-09 | - DISCARD RECORD / DUPLICATE | 43432090 | 83386 | Oliver James | 17 | 2010-08-09 | + KEEP | 43432092 | 83386 | Oliver James | 0 | 2010-08-09 | - DISCARD RECORD / DUPLICATE | 43432092 | 83386 | Oliver James | 17 | 2010-08-09 | + KEEP | 43470093 | 83386 | Oliver James | 0 | 2010-08-09 | - DISCARD RECORD / DUPLICATE | 43470093 | 83386 | Oliver James | 17 | 2010-08-09 | - DISCARD RECORD / DUPLICATE | 43470093 | 83386 | Oliver James | 35 | 2010-08-09 | - DISCARD RECORD / DUPLICATE | 43470093 | 83386 | Oliver James | 52 | 2010-08-09 | - DISCARD RECORD / DUPLICATE | 43470093 | 83386 | Oliver James | 69 | 2010-08-09 | - DISCARD RECORD / DUPLICATE | 43470093 | 83386 | Oliver James | 87 | 2010-08-09 | - DISCARD RECORD / DUPLICATE | 43470093 | 83386 | Oliver James | 100 | 2010-08-09 | + KEEP +----------+------------------+--------------+--------------+------------+



表格和原始数据:

CREATE TABLE `log_embed_video` (
  `id` int(11) NOT NULL,
  `archive_asset_id` int(11) NOT NULL,
  `asset_title` varchar(255) NOT NULL,
    `view_percent` float NOT NULL,
  `created` int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;


INSERT INTO `log_embed_video` VALUES 
(43326898, 83386, 'Oliver James', 0, 1281327306),
(43326898, 83386, 'Oliver James', 13, 1281327327),
(43432090, 83386, 'Oliver James', 0, 1281371423),
(43432090, 83386, 'Oliver James', 17, 1281371445),
(43432092, 83386, 'Oliver James', 0, 1281371424),
(43432092, 83386, 'Oliver James', 17, 1281371446),
(43470093, 83386, 'Oliver James', 0, 1281380789),
(43470093, 83386, 'Oliver James', 17, 1281380810),
(43470093, 83386, 'Oliver James', 35, 1281380830),
(43470093, 83386, 'Oliver James', 52, 1281380850),
(43470093, 83386, 'Oliver James', 69, 1281380871),
(43470093, 83386, 'Oliver James', 87, 1281380891),
(43470093, 83386, 'Oliver James', 100, 1281380906);

【问题讨论】:

  • 我在里面找不到问题 - 我真的不知道你在问什么。
  • +1 用于示例数据并创建表...但是(对我而言)您想要实现的目标仍然不是很清楚(尝试用文字解释“期望的结果”;只是盯着数据没有帮助我理解你想要达到的目标)
  • 代码应该如何处理跨越到第二天(即:23:59 => 50 % / 00:01 => 55%)?
  • 我不确定是否过滤掉重复数据。参考条件数据。我有 4 行要保留,过滤掉所有其他行,然后能够使用聚合函数并对过滤后的数据进行分组。
  • @ Wrikken - 我需要过滤掉重复的行...例如,有 2 条记录的 id 为 43326898。包含该 id 的行和 view_percent 列的最大值/最大值是我要过滤的内容

标签: mysql group-by aggregate-functions


【解决方案1】:

唯一 id 的所有 max-percentage per-id 行:

SELECT a.* 
FROM log_embed_video a 
LEFT JOIN log_embed_video b
ON b.id = a.id
AND b.view_percent > a.view_percent
WHERE b.id IS NULL
-- possibly limit on date for  more performance.

在性能方面这更好:

SELECT * FROM (
    SELECT id, archive_asset_id, asset_title, view_percent, created,
        @rn := IF(id != @old_id,1,@rn + 1) as rownumber,
        @old_id := id 
    FROM log_embed_video 
    JOIN (SELECT @rn:=0,@old_id:=0) void
    ORDER BY id, view_percent DESC
) a WHERE rownumber=1;

【讨论】:

  • +1 因为它有效 :) 我想知道 Scott 会发现哪种解决方案更容易理解......
  • 如果不熟悉 LEFT JOIN 技巧,可读性会受到影响。它在性能方面也受到了一些影响。我可能会输入一个更快的更难以理解的答案......
  • 那里,现在所有的可读性都在窗外有利于性能:P
  • @Wrikken - 感谢提交此查询,但这不会产生预期的结果。 Unreason 提交的两个修改后的查询都适用于我想要完成的工作。谢谢!!!
  • @Scott:这些是通用的“我想要一组具有 MIN/MAX 值的行,以及来自同一行的相关数据”。确实不是您想要的完整查询,但两者都会产生您提到的正确“条件数据”,并且在任何人(My)SQL 的工具集中都很方便,尤其是第二个的性能(至少在普通 InnoDB / MyISAM 表上) .不过不用担心,Unreasons 的回答确实可以正常工作。
【解决方案2】:

检查这是否会让你更清楚

SELECT archive_asset_id, AVG(actual_percent) 
FROM (SELECT id, archive_asset_id, asset_title, 
             MAX(view_percent) as actual_percent 
      FROM log_embed_video GROUP by id) T 
GROUP BY archive_asset_id;

返回:

+------------------+---------------------+
| archive_asset_id | AVG(actual_percent) |
+------------------+---------------------+
|            83386 |               36.75 | 
+------------------+---------------------+

一些笔记

  • 这在 1 亿条记录上表现不佳
  • 您还可能希望对数据进行规范化以提高性能(在这种情况下会这样做;基本上将实际的最终行移到它们自己的表中对我来说更有意义)
  • COUNT(DISTINCT id * 1000000 + archive_asset_id) 这个表达式引起了我的注意,因为它很奇怪;你确定你的意思不是简单的COUNT(*)COUNT(id)

编辑:

第二个

SELECT archive_asset_id, actual_percent, count(*) 
FROM (SELECT id, archive_asset_id, asset_title,               
             MAX(view_percent) as actual_percent        
      FROM log_embed_video GROUP by id) T  
GROUP BY archive_asset_id, actual_percent;

+------------------+----------------+----------+
| archive_asset_id | actual_percent | count(*) |
+------------------+----------------+----------+
|            83386 |             13 |        1 | 
|            83386 |             17 |        2 | 
|            83386 |            100 |        1 | 
+------------------+----------------+----------+

【讨论】:

  • COUNT(DISTINCT id * 1000000 + archive_asset_id) 我认为到达COUNT(DISTINCT id,archive_asset_id) 是一个难题
  • 我们正在使用 Infobright Brighthouse 数据库,该数据库专为查询大型数据集而设计。目前该数据库不支持 COUNT(DISTINCT col1, col2) 所以这是他们推荐的解决方法
  • @Unreason - 这两个修改后的查询都工作得很好。当按日期范围和单个资产 ID 过滤数据时,结果会在不到 200 毫秒的时间内返回,此表中有超过 100+ 百万条记录。感谢您花时间回答我的问题!
  • @Scot;啊,我看到了 DISTINCT 的逻辑.. 好吧,现在也没有必要了.. (PS 见 faq revoting)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-07-14
  • 1970-01-01
相关资源
最近更新 更多