PostgreSQL - получение статистических данных - PullRequest
9 голосов
/ 07 февраля 2012

Мне нужно собрать статистическую информацию в моем приложении.У меня есть таблица пользователей (tb_user). Каждый раз, когда новый пользователь обращается к приложению, он добавляет новую запись в эту таблицу, то есть одну строку для каждого пользователя.Основное поле: id и date_hour (временная метка при первом обращении пользователя к приложению).

tb_user

id (bigint) | date_time (timestamp with time zone)
 1          |  2012-01-29 11:29:50.359-03
 2          |  2012-01-31 14:27:10.359-03

Мне нужно получить:

количество средних пользователей по дням, неделям и месяцам

Пример:

по дням: 55,45

по неделям: XX.XX

месяц: XX.XX

РЕДАКТИРОВАТЬ:

Моим лучшим решением было:

WITH daily_count AS (SELECT COUNT(id) AS user_count FROM tb_user)
SELECT user_count, tbaux2.days, (user_count/tbaux2.days) FROM daily_count, 
    (SELECT EXTRACT(DAY FROM (t2.diff) ) + 1 AS days
     FROM
       (with tbaux AS(SELECT  min(date_time) AS min FROM tb_user)
       SELECT (now() - min) AS diff
       FROM tbaux) AS t2) AS tbaux2
GROUP BY user_count, tbaux2.days

Но это решение работало только с EXTRACT (ДЕНЬ ... С неделями и месяцами не работало

Любая помощь приветствуется.

В качестве альтернативы:

SELECT user_count, tbaux2.days, (user_count/tbaux2.days) AS userPerDay, ((user_count/tbaux2.days) * 7) AS userPerWeek, ((user_count/tbaux2.days) * 30) AS userPerMonth

РЕДАКТИРОВАТЬ 2:

Основываясь на ответах @Bruno, есть несколько соображений:

Когда я задал вопросЯ действительно запрашивал способ выбора данных по дням, месяцам и годам. Я считаю, что поиск, который я опубликовал и уточнил @Bruno, должен интерпретироваться как среднее значение "в день, каждые 7 дней и каждые 30 дней" а не по дням, неделям и месяцы.Я полагаю, что если это будет интерпретировано таким образом, то не будет проблем, указанных в примере (например, снижение на 10%).Я полагаю, что подход «каждый» - это ответ, который мне нужен в данный момент, поэтому подпишу этот ответ.

Я предлагаю в качестве улучшения сообщения:

  • В качестве результата рассмотрим только закрытый день.(не собирать пользователей текущего дня и не считая текущий день в делении)
  • В результате получаются две цифры.
  • Новое исследование, учитывающее данные действительно за неделю и за месяц.

Спасибо.

1 Ответ

18 голосов
/ 07 февраля 2012

Вы должны посмотреть на агрегатные функции (min, max, count, avg), которые идут рука об руку с GROUP BY.Для агрегирования по датам также полезно date_trunc.

Например, при этом будет возвращаться количество строк в день:

SELECT date_trunc('day', date_time) AS day_start,
       COUNT(id) AS user_count FROM tb_user
    GROUP BY date_trunc('day', date_time);

Затем можносделать среднесуточное значение, используя что-то вроде этого (с a CTE ):

WITH daily_count AS (SELECT date_trunc('day', date_time) AS day_start,
       COUNT(id) AS user_count FROM tb_user
    GROUP BY date_trunc('day', date_time))
SELECT AVG(user_count) FROM daily_count;

Используйте 'week' вместо дня для подсчета за неделю и т. д. (см. документацию date_trunc)).

РЕДАКТИРОВАТЬ: (следующий комментарий: в среднем до 5/1/2012 включительно, т.е. до 6-го числа.)

WITH daily_count AS (SELECT date_trunc('day', date_time) AS day_start,
       COUNT(id) AS user_count
    FROM tb_user
       WHERE date_time >= DATE('2012-01-01') AND date_time < DATE('2012-01-06') 
    GROUP BY date_trunc('day', date_time))
SELECT SUM(user_count)/(DATE('2012-01-06') - DATE('2012-01-01')) FROM daily_count;

То, что выше, закончилосьСложно, в этом случае.Это должно дать вам тот же результат:

SELECT COUNT(id)/(DATE('2012-01-06') - DATE('2012-01-01'))
    FROM tb_user
       WHERE date_time >= DATE('2012-01-01') AND date_time < DATE('2012-01-06');

РЕДАКТИРОВАТЬ 2: После вашего редактирования, я думаю, что вы ищете, это всего лишь одно глобальное среднее значение за весь период существованияваша база данных, а не группы по месяцам / неделям / дням.

Это должно дать вам среднее количество строк в день:

WITH total_min_max AS (SELECT
        COUNT(id) AS total_visits,
        MIN(date_time) AS first_date_time,
        MAX(date_time) AS last_date_time,
    FROM tb_user)
SELECT total_visits/((last_date_time::date-first_date_time::date)+1) AS users_per_day
    FROM total_min_max

(я бы заменил last_date_time на NOW() чтобы получить среднее значение по времени до настоящего момента, а не до последнего посещения, если нет недавнего посещения.)

Затем для ежедневных, еженедельных и "ежемесячных":

WITH daily_avg AS (
    WITH total_min_max AS (SELECT
            COUNT(id) AS total_visits,
            MIN(date_time) AS first_date_time,
            MAX(date_time) AS last_date_time,
        FROM tb_user)
    SELECT total_visits/((last_date_time::date-first_date_time::date)+1) AS users_per_day
        FROM total_min_max)
SELECT
         users_per_day,
         (users_per_day * 7) AS users_per_week,
         (users_per_month * 30) AS users_per_month
    FROM daily_avg

При этом, выводы, которые вы делаете из такой статистики, могут быть не очень хорошими, особенно если вы хотите увидеть, как она меняется.

Я бы также нормализовал данные за день, а не предполагал, что 30 дней в месяце (если не в час, потому что не во всех днях есть 24 часа ).Скажем, у вас есть 10 посещений в день в январе 2011 года и 10 посещений в день в феврале 2011 года. Это дает вам 310 посещений в январе и 280 посещений в феврале. Если вы не обращаете внимания, вы можете подумать, что у вас было почтиКоличество посетителей уменьшилось на 10%, поэтому в феврале что-то пошло не так, хотя на самом деле это не так.

...