2

我正在尝试在 Rails 中进行队列分析查询,但在使用按最后操作日期分组的正确方法时遇到了麻烦。

我想以如下数据行结束:http ://www.quickcohort.com/

count first_action last_action

适用于去年注册的所有用户。first_action并被last_action截断到最近的月份。

获得按 分组的计数first_action很容易,但是当我尝试将其扩展为包括last_action我遇到的

ActiveRecord::StatementInvalid: PGError: ERROR:  aggregates not allowed in GROUP BY clause

这是我到目前为止所拥有的

User
  .select("COUNT(*) AS count,
    date_trunc('month', users.created_at) AS first_action,
    MAX(date_trunc('month', visits.created_at)) AS last_action # <= Problem
  ")
  .joins(:visits)
  .group("first_action, last_action") # TODO: Subquery ?
  .order("first_action ASC, last_action ASC")
  .where("users.created_at >= date_trunc('month', CAST(? AS timestamp))", 12.months.ago)

访问表跟踪用户对站点的所有访问。使用最新访问作为最后一个操作似乎应该很容易,但我在将其形成 SQL 时遇到了麻烦。

如果有更好的方法,我也愿意接受其他解决方案,但似乎单个 SQL 查询的性能最高。

4

1 回答 1

2

我认为您需要在子查询中执行此操作。就像是:

select first_action, last_action, count(1)
from (
    select
        date_trunc('month', visits.created_at) as first_action,
        max(date_trunc('month', visits.created_at)) as last_action
    from visits
    join users on users.id = visits.user_id
    where users.created_at >= ?
    group by user_id
)
group by first_action, last_action;

我不确定在 ARel 中最优雅的方法是什么,但我认为它会是这样的。(直接使用 SQL 可能更容易。)

def date_trunc_month(field)
  Arel::Nodes::NamedFunction.new(
    'date_trunc', [Arel.sql("'month'"), field])
end

def max(*expressions)
  Arel::Nodes::Max.new(expressions)
end

users = User.arel_table
visits = Visit.arel_table

user_visits = visits.
    join(users).on(visits[:user_id].eq(users[:id])).
    where(users[:created_at].gteq(12.months)).
    group(users[:id]).
    project(
        users[:id],
        date_trunc_month(visits[:created_at]).as('first_visit'),
        max(date_trunc_month(visits[:created_at])).as('last_visit')
    ).
    as('user_visits')

cohort_data = users.
    join(user_visits).on(users[:id].eq(user_visits[:id])).
    group(user_visits[:first_visit], user_visits[:last_visit]).
    project(
        user_visits[:first_visit],
        user_visits[:last_visit],
        Arel::Nodes::Count.new([1]).as('count')
    )
于 2013-01-01T20:07:42.733 回答