2012-03-20 2 views
5

Учитывая, что с помощью метки времени (которые представляют собой проверки в моем приложении) с полем временной отметки, что было бы хорошим способом определить текущую последовательность последовательных проверок?PostgreSQL: найдите число дней подряд до

Другими словами, с помощью проверок, отсортированных по времени регистрации, сколько записей осталось до тех пор, пока пользователь не пропустил день?

В настоящее время я использую эту технику:

SELECT distinct(uca.created_at::date) as created_at 
    FROM user_challenge_activities as uca INNER JOIN user_challenges as uc 
    ON user_challenge_id = uc.ID WHERE uc.user_id = #{user.id} 
    order by (uca.created_at::date) DESC; 

... где я бросить фиксирование метки времени к дате (в конечном итоге, например, 2012-03-20), то в коде, пройти через записи и увеличивать счетчик до тех пор, пока дата между записью и следующей записью не превысит 1 день.

Однако этот подход кажется неуклюжим для меня, и кажется, что это то, что преграждает Postgres.

Итак, действительно ли лучший способ достичь этого?

+0

Определите «текущий» в «текущей последовательности последовательных проверок». Вы имеете в виду полосу, включающую сегодня? Или просто последняя полоса? –

+0

Ах да, текущая серия последовательных регистраций, включая сегодня. По существу, количество последовательных дней, включая сегодня, которые вы проверили в – yalestar

ответ

4
with t as (
    SELECT distinct(uca.created_at::date) as created_at 
    FROM user_challenge_activities as uca 
    INNER JOIN user_challenges as uc ON user_challenge_id = uc.ID 
    WHERE uc.user_id = #{user.id} 
    ) 
select count(*) 
from t 
where t.create_at > (
    select d.d 
    from generate_series('2010-01-01'::date, CURRENT_DATE, '1 day') d(d) 
    left outer join t on t.created_at = d.d::date 
    where t.created_at is null 
    order by d.d desc 
    limit 1 
) 
+0

Теперь это маленький кусочек удивительного! Один вопрос: каково значение 2010-01-01? – yalestar

+1

Я думаю, что CTE нуждается в скобках вокруг него: 'с t as (...)' –

+0

@a_horse_with_no_name Исправлено –

1

давайте попробуем это снова. будет генерировать только ряд для необходимого диапазона:

SELECT count(distinct(uca.created_at::date)) FROM user_challenge_activities as uca 
JOIN 
    (SELECT generate_series(max(series_date), 
         (select max(user_challenge_activities.created_at) 
          FROM user_challenge_activities), '1 day') as datez 
    FROM 
    (SELECT generate_series(min(user_challenge_activities.created_at::date), 
          max(user_challenge_activities.created_at), '1 day')::date 
      as series_date 
     FROM user_challenge_activities) x 
    LEFT JOIN user_challenge_activities 
    ON (user_challenge_activities.created_at::date = x.series_date) 
    WHERE created_at IS NULL) d ON d.datez = uca.created_at::date 
INNER JOIN user_challenges as uc ON user_challenge_id = uc.ID 
WHERE uc.user_id = #{user.id}; 
Смежные вопросы