MYSQL按组加权平均值

时间:2016-11-08 00:30:21

标签: mysql database query-optimization

我有一个具有以下结构的mysql表

| Field          | Type         | 
+----------------+--------------+
| Data           | timestamp    | 
| ticker         | varchar(250) |                                        
| sentiment      | double       |                                            
| numberofTweets | int(11)      |

有70种不同的代码,我需要计算每种代号的加权平均值。

我正在做以下事情:

select ticker, round(sum(sentiment)/sum(numberofTweets),2) as wAverage
from sentiment
WHERE ticker = 'GBP/USD'
order by data desc
limit 288;

在单个查询中是否有办法处理所有代码?

提前Tks!

编辑1:

我需要为每个自动收报机使用288条记录进行计算,因此使用GROUP BY clausule将无效!

1 个答案:

答案 0 :(得分:1)

为了简化您的问题,我使用下表:

CREATE TABLE `items` (
    `id` MEDIUMINT(8) UNSIGNED NOT NULL,
    `group_id` TINYINT(3) UNSIGNED NOT NULL,
    `val` DOUBLE UNSIGNED NOT NULL DEFAULT '0',
    PRIMARY KEY (`id`),
    INDEX `group_id` (`group_id`)
) ENGINE=InnoDB;

测试数据包含100个组,每组平均1000个项目(总共100K行)。

set @num_rows = 100000;
set @per_group = 1000;
set @num_groups = @num_rows div @per_group;

insert into items (id, group_id, val)
    select seq as id
        , floor(rand(1)*@num_groups) + 1 as group_id
        , rand(2) as val
    from seq_1_to_1000000
    where seq <= @num_rows
;

任务:val获取最新288行group_id的总和。

不需要每组288行,它只是

select group_id, sum(val)
from items
group by group_id

但首先,您需要将数据集限制为每组288行。每组搜索&#34; mysql top n&#34;你会在SO上找到很多相关的问题。

大多数答案都会使用会话变量:

select group_id, sum(val)
from (
    select i.group_id, i.val,
        case when i.group_id = @group 
            then @rn:=@rn+1
            else @rn:=1
        end rn,
        @group := i.group_id 
    from items i
    cross join (select @rn := null, @group := null) init_vars
    order by i.group_id, i.id desc
) t
where rn <= 288
group by group_id
order by group_id

查询时间:62 - 78 ms(我的客户没有显示确切的数字)。但是 - 使用此解决方案,您依赖于引擎的执行顺序,这可能会在将来的版本中发生变化。

其他一些答案使用selfjoin:

select group_id, sum(val)
from (
    select i.group_id, i.val
    from items i
    join items i1
        on  i1.group_id = i.group_id
        and i1.id >= i.id
    group by i.id
    having count(*) <= 288
) t
group by group_id
order by group_id

但性能仅适用于小组(平均组大小<= 10)。查询时间:17秒

还有一个&#34;技巧&#34;使用SUBSTRING_INDEX(GROUP_CONCAT(...), ...),可能需要为大型群组调整@@group_concat_max_len。 但我更喜欢这个:

select i.group_id, sum(i.val)
from items i
where i.id >= coalesce((
    select i1.id
    from items i1
    where i1.group_id = i.group_id
    order by i1.id desc
    limit 1
    offset 287
), 0)
group by i.group_id
order by i.group_id

它首先在相关子查询中找到每组第288个最高的id,并且仅使用具有更高或相等ID的行。如果组的行少于288行,则将使用所有行(id> = 0)。 查询时间:78 - 94 ms。

根据您的问题进行调整,您将获得:

select s.ticker, round(sum(sentiment)/sum(numberofTweets),2) as wAverage
from sentiment s
where s.data >= coalesce((
    select s1.data 
    from sentiment s1
    where s1.ticker = s.ticker
    order by s1.data desc
    limit 1
    offset 287
), from_unixtime(0))
group by s.ticker
order by s.ticker

请注意,如果按非唯一列对结果进行排序,则无法很好地定义限制结果集。因此,如果两个时间段相等,它可能会使用289行或更多行。使用AUTO_INCREMENT PRIMARY KEY更好。