2012-12-29 5 views
2

레일스에서 ​​코호트 분석 쿼리를 수행하려고하지만 마지막 작업 날짜별로 그룹화하는 올바른 방법으로 문제가 발생합니다.레일스 코호트 분석

나는 이런 일에 대해 다음 데이터 행과 함께 종료 할 : 작년에 등록 된 모든 사용자에 대해 http://www.quickcohort.com/

count first_action last_action 

. first_actionlast_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 쿼리가 가장 효과적 인 것처럼 보입니다.

답변

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') 
    )