SQl запрос для подсчета количества активных пользователей в конце дня - PullRequest
0 голосов
/ 21 января 2020

У меня три столбца User_ID, New_Status and DATETIME.

New_Status содержит 0 (неактивно) и 1 (активно) для пользователей. Каждый пользователь начинает с активного статуса - ie. 1. Впоследствии в таблице хранится их статус и datetime, при котором они были активированы / деактивированы.

Как рассчитать количество активных пользователей на конец каждой даты, включая даты, когда в таблицу не было сгенерировано ни одной записи.

Пример данных:

| ID | New_Status |      DATETIME       |
+----+------------+---------------------+
| 1  |      1     | 2019-01-01 21:00:00 |
| 1  |      0     | 2019-02-05 17:00:00 |
| 1  |      1     | 2019-03-06 18:00:00 |
| 2  |      1     | 2019-01-02 01:00:00 |
| 2  |      0     | 2019-02-03 13:00:00 |

Ответы [ 6 ]

3 голосов
/ 21 января 2020

Форматировать значение даты и времени в строку только с датой и группировать по ней

SELECT DATE_FORMAT(DATETIME, '%Y-%m-%d') as day, COUNT(*) as active
FROM test
WHERE New_Status = 1
GROUP BY day
ORDER BY day
1 голос
/ 21 января 2020

Узнайте последнее состояние активности пользователей, чья активность менялась за каждый день

select User_ID, New_Status, DATE_FORMAT(DATETIME, '%Y-%m-%d')
from activity_table
where not exists
(
    select 1
    from activity_table at
    where at.User_ID = activity_table.User_ID and
          DATE_FORMAT(at.DATETIME, '%Y-%m-%d') = DATE_FORMAT(activity_table.DATETIME, '%Y-%m-%d') and
          at.DATETIME > activity_table.DATETIME
)
order by DATE_FORMAT(activity_table.DATETIME, '%Y-%m-%d');

Это еще не решение, но очень полезная информация перед решением. Обратите внимание, что здесь еще не все даты охвачены, и значения являются отдельными записями, точнее их последними значениями на каждый день, упорядоченными по дате.

Давайте получим агрегированные числа

Используя запрос выше как подвыбор и наложение его на table, вы можете group by DATETIME и выполнить select sum(new_Status) as activity, count(*) total, DATETIME, чтобы вы знали, что activity - (total - activity) - это разница по сравнению с предыдущим днем.

Зная дельту для каждого дня, присутствующего в результате

В предыдущем разделе мы видели, как можно вычислить дельту. Если весь запрос в предыдущем разделе является псевдонимом, вы можете самостоятельно присоединиться к нему, используя левое соединение, с парами (предыдущая дата, текущая дата), по-прежнему сохраняя пропуски дат, но пока не беспокоясь об этом. В случае первого свидания его activity является дельтой. Для последующих записей добавление дельты предыдущего дня к их дельте дает нужный вам результат. Для этого вы можете использовать рекурсивный запрос, поддерживаемый MySQL 8, или, альтернативно, вы можете просто иметь подзапрос, который суммирует дельту предыдущих дней (с особым вниманием к первой дате, как описано ранее) и добавляя дельта текущей даты дает нужный нам результат.

Заполнить пробелы

Предыдущий раздел уже отлично работал бы (предполагая отсутствие проблем с целостностью), предполагая, что в каждый день происходили изменения активности , но мы не будем продолжать с предположением. Здесь мы знаем, что цифры верны для каждой даты, где фигура присутствует, и нам нужно будет просто добавить отсутствующие даты в результат. Если результаты правильно упорядочены, как и должно быть, то можно использовать курсор и l oop результаты. По каждой записи после первой мы можем определить пропущенные даты. Может быть 0 таких дат между двумя последовательными датами или более. Что мы знаем о пробелах, так это то, что их значения точно такие же, как в предыдущей записи, в которой есть данные. Если в указанную дату не было изменений активности, то количество активных пользователей точно такое же, как и в предыдущий день. Используя некоторую структуру, например таблицу, вы можете генерировать результаты, которые вы получаете, используя знания, описанные здесь.

Решение возможных проблем целостности

Существует несколько возможностей для таких проблем:

Во-первых, элемент данных мог существовать до того, как начали появляться записи этой таблицы.

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

В-третьих, добавление пользователя вызывает или не обязательно приводит к изменению активности, поскольку его возникновение делает его предыдущее состояние активности неопределенным и подчиняется человеческим стандартам, которые могут меняться со временем.

Четвертый удаление пользователя создает или не обязательно генерирует изменение активности, поскольку его вытеснение из существования делает текущее состояние активности неопределенным и подчиняется человеческим стандартам, которые могут меняться со временем.

В-пятых, существует бесконечность других вопросов, которые ми Это может вызвать проблемы с целостностью данных.

Чтобы справиться с ними, вам потребуется всесторонне проанализировать все, что вы можете из исходного кода и истории проекта, включая записи в базе данных, журналы и доступную человеку информацию для обнаружения таких аномалий. время, когда они были эффективными, и выясните, каково их решение, если они существуют.

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

Тем временем я думал о возможности пользователя, который был активен в начале день будет деактивирован, а затем снова активирован к концу дня. Аналогичным образом, неактивный пользователь в течение дня может быть активирован, а затем окончательно деактивирован к концу дня. Для пользователей, у которых в начале дня было больше, чем активация, нам нужно сравнить их статус активности в начале и в конце дня, чтобы выяснить, в чем разница.

1 голос
/ 21 января 2020

В MySQL 8 вы можете использовать оконную функцию row_number(), чтобы получить последний статус пользователя в день. Затем отфильтруйте тот, который указывает, что пользователь был активным GROUP BY день, и сосчитайте их.

SELECT date(x.datetime),
       count(*)
       FROM (SELECT date(t.datetime) datetime,
                    t.new_status,
                    row_number() OVER (PARTITION BY date(t.datetime)
                                       ORDER BY t.datetime DESC) rn
                    FROM elbat t) x
       WHERE x.rn = 1
             AND x.new_status = 1
       GROUP BY x.datetime;

Если в таблице не все дни, вам нужно создать (возможно, производную) таблицу со всеми днями и кросс присоединиться к нему.

0 голосов
/ 21 января 2020

Вот что я считаю хорошим решением для вашей проблемы:

SELECT SUM(New_Status) "Number of active users"
       , DATE_FORMAT(DATEC, '%Y-%m-%d') "Date"
FROM TEST T1
WHERE DATE_FORMAT(DATEC,'%H:%i:%s') = 
      (SELECT  MAX(DATE_FORMAT(T2.DATEC,'%H:%i:%s'))
       FROM TEST T2
       WHERE T2.ID = T1.ID 
       AND  DATE_FORMAT(T1.DATEC, '%Y-%m-%d') =  DATE_FORMAT(T2.DATEC, '%Y-%m-%d')
       GROUP BY  ID
                 , DATE_FORMAT(DATEC, '%Y-%m-%d'))
GROUP BY DATE_FORMAT(DATEC, '%Y-%m-%d');

Вот ДЕМО

0 голосов
/ 21 января 2020
WITH RECURSIVE 
cte AS (
SELECT MIN(DATE(DT)) dt 
FROM src
UNION ALL
SELECT dt + INTERVAL 1 DAY 
FROM cte 
WHERE dt < ( SELECT MAX(DATE(DT)) dt 
             FROM src )
),
cte2 AS 
(
SELECT users.id, 
       cte.dt, 
       SUM( CASE src.New_Status WHEN 1 THEN 1
                                WHEN 0 THEN -1
                                ELSE 0 
                                END ) OVER ( PARTITION BY users.id
                                             ORDER BY cte.dt ) status
FROM cte
CROSS JOIN ( SELECT DISTINCT id
             FROM src ) users
LEFT JOIN src ON src.id = users.id
             AND DATE(src.dt) = cte.dt
)
SELECT dt, SUM(status)
FROM cte2
GROUP BY dt;

скрипка

Не забудьте настроить максимальную глубину рекурсии.

0 голосов
/ 21 января 2020
SELECT 
    DATE(DATETIME),
    COUNT(*)
FROM your_table
WHERE New_Status = 1
GROUP BY User_ID,
         DATE(DATETIME)

Для MySQL

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...