涉及多态关联的复杂时间序列统计聚合

时间:2011-06-04 19:00:27

标签: mysql sql ruby-on-rails polymorphic-associations aggregation

确定。请耐心等待,因为在我能够对我的问题给出合理的答案之前,我需要提供大量的背景细节。

我有一个网站,允许您每日选股。它的工作方式是,提示您在当天面临关闭的公司之间进行选择。例如,GE与IBM。您可以选择两种类型:性能(哪种股票表现更好?)和总成交量(合并后的股票交易量是否高于或低于X?)。你每天获得100美元的虚拟美元来进行选择。

最终,我们的目标是在以下时间段内跟踪哪个用户在不同类别(下面解释)中每次选择赚取最多钱:5天,15天,30天,90天,180天,1年, 整天。计算每个选择的金额非常简单。这是总投入(或丢失)/选秀数量。

现在,用户选择的每个公司都属于分类层次结构。通常,分类层次结构如下所示:

分部 - >主要群组 - >工业集团 - >分类 - >公司

以下是一些例子:

  • 采矿 - >金属采矿 - >铁矿石 - > Brown Ore Mining - >公司A
  • 采矿 - >金属采矿 - >铁矿石 - > Brown Ore Mining - >公司B
  • 采矿 - >金属采矿 - >铁矿石 - >褐铁矿采矿 - >公司C
  • 采矿 - >金属采矿 - >铁矿石 - >褐铁矿采矿 - >公司D
  • 制造 - >烟草产品 - >雪茄 - > Stogies - >公司E
  • 制造 - >烟草产品 - >雪茄 - > Stogies - >公司F
  • 制造 - >烟草产品 - >雪茄 - > Cigarillos - >公司G
  • 制造 - >烟草产品 - >雪茄 - > Cigarillos - >公司H
  • ......依旧......

每个类别都有一个模型(当然还有相应的表格),它们与你上面所说的相关联(想想foreign_key)。

Matchup有一个模型,每条记录代表当天哪些公司正面临关闭。每条记录都会跟踪每家公司的起始和最终股票价格,以及总交易量。

每个Matchup都有一个或多个:pick_prices,可以全天更改。通常,每场比赛都有一个表现选择价格和一个总体积选择价格。价格决定了你选择的费用以及正确选择的收入。 (现在,这只是背景信息。您无需担心这些特定的价格计算。)

在交易日结束时,用户的选择被解决。选择在Pick模型中表示,具有以下属性:

  • USER_ID
  • amount_spent(例如,10美元)
  • 结果(例如,WON,LOST)
  • 选择(例如,公司A)
  • matchup_id
  • pick_price_id
  • amount_won
  • 已解决(真或假)
  • created_at
  • 的updated_at

目前,当解析每个选秀权时,会更新另一个名为pick_records的表,该表具有以下属性:

  • USER_ID
  • recordable_id
  • recordable_type(分部或主要集团或行业集团或分类或公司)
  • 选秀权(总选秀权,不论选秀类型)
  • 赢了(赢得总票数,不论选秀类型)
  • 丢失(总选秀权丢失,不论选择类型)
  • 钱(赢得的总金额)
  • money_per_pick(钱/选秀权)
  • performance_picks
  • performance_won
  • performance_lost
  • performance_money
  • performance_money_per_pick
  • volume_picks
  • volume_won
  • volume_lost
  • volume_money
  • volume_money_per_pick
  • created_at
  • 的updated_at

正如您所知,这是一个多态模型。该表聚合了所有时间选择记录统计信息。

所以现在是挑战:

鉴于现有设计,我必须做些什么才能在以下时间段内捕获用户的选择记录:5天,15天,30天,90天,180天,1年,全部 - 时间?它需要简单,高效,快速!

我目前正在MySQL DB上运行Rails 2.3.11。

3 个答案:

答案 0 :(得分:3)

我认为不需要表pick_records 您可以在任意天数内执行此类查询:

SELECT 
   user_id
   ,sum(amount_spent) 
   ,sum(IF(result = 'WON',1,0)) as WON_count
   ,sum(IF(result = 'LOST',1,0)) as LOST_count
   ,pick 
   /*matchup_id*/
   ,sum(pc.price) as price
   ,sum(IF(result = 'WON'),amount_won,0)) as amount_won
   ,sum(IF(result = 'LOST'),amount_won,0)) as amount_lost
   ,sum(IF(result = 'WON'),amount_won,-amount_won)) as nett_amount
FROM picks
INNER JOIN pick_price pc ON (pc.id = user.pick_price_id)
WHERE created_at BETWEEN DATE_SUB(NOW(), INTERVAL 5 DAY) AND NOW()
  AND resolved = 'true'
GROUP BY user_id, pick

答案 1 :(得分:0)

不确定我的问题是否正确,但是......

@records=Pick_record.all(:conditions => ["user_id = ?", user_id],
                         :group => "date(created_at)", 
                         :having => ["created_at > ?", 5.days.ago])

答案 2 :(得分:0)

如果我理解正确,现在每个用户只有一个pick_record,并且它包含了他的总选秀权的概述,并在解决选择时更新。

由于可以计算pick_record的内容,因此它仅用于缓存,并确保您可以非常快速地提供数据/报告。

要解决您的问题,我建议如下:

在整个生命周期中,我会有一个pick_record,而不是单个pick_record。我会对你感兴趣的每个时间段有一个pick_record。所以你最后4天会有一个pick_record结果,一个结果是结果过去14天,29 ...那些你每天计算一次,最好是晚上(或当你的网站使用率低的时候)。当必须显示选定时间段的报告时,您只需要添加当天的结果并完成!

所以,回顾一下:

  1. 每个感兴趣的时段引入一个pick_record(添加一个表示句号的字段:5,15,30 ......)
  2. 每天预先计算一次结果(后台工作,e.q。resque或delayed_job)
  3. 检索期间结果时,您只需要添加当天的结果
  4. 您怎么看?