Mysql
 sql >> Base de Dados >  >> RDS >> Mysql

Contar usuários ativos usando timestamp de login no MySQL


Vou demonstrar tal ideia com base no que faz mais sentido para mim e na forma como responderia se a pergunta fosse apresentada da mesma forma que aqui:

Primeiro, vamos supor um conjunto de dados como tal, nomearemos a tabela logins :
+---------+---------------------+
| user_id |   login_timestamp   |
+---------+---------------------+
|       1 | 2015-09-29 14:05:05 |
|       2 | 2015-09-29 14:05:08 |
|       1 | 2015-09-29 14:05:12 |
|       4 | 2015-09-22 14:05:18 |
|   ...   |          ...        |
+---------+---------------------+

Pode haver outras colunas, mas não nos importamos com elas.

Antes de tudo devemos determinar as bordas daquela semana, para isso podemos usar ADDDATE() . Combinado com a ideia de que a data de hoje é o dia da semana de hoje (DAYOFWEEK() do MySQL ), é a data de domingo.

Por exemplo:Se hoje for quarta-feira, 10, Wed - 3 = Sun , portanto 10 - 3 = 7 , e podemos esperar que domingo seja dia 7.

Podemos obter WeekStart e WeekEnd timestamps desta forma:
SELECT
DATE_FORMAT(ADDDATE(CURDATE(), INTERVAL 1-DAYOFWEEK(CURDATE()) DAY), "%Y-%m-%d 00:00:00") WeekStart, 
DATE_FORMAT(ADDDATE(CURDATE(), INTERVAL 7-DAYOFWEEK(CURDATE()) DAY), "%Y-%m-%d 23:59:59") WeekEnd;

Nota:no PostgreSQL existe um DATE_TRUNC() função que retorna o início de uma unidade de tempo especificada, dada uma data, como início da semana, mês, hora e assim por diante. Mas isso não está disponível no MySQL.

Em seguida, vamos utilizar WeekStart e weekEnd para clicar em nosso conjunto de dados, neste exemplo mostrarei apenas como filtrar, usando datas codificadas:
SELECT *
FROM `logins`
WHERE login_timestamp BETWEEN '2015-09-29 14:05:07' AND '2015-09-29 14:05:13'

Isso deve retornar nosso conjunto de dados fatiado, com apenas resultados relevantes:
+---------+---------------------+
| user_id |   login_timestamp   |
+---------+---------------------+
|       2 | 2015-09-29 14:05:08 |
|       1 | 2015-09-29 14:05:12 |
+---------+---------------------+

Podemos então reduzir nosso conjunto de resultados para apenas o user_id s e filtre as duplicatas. então conte, desta forma:
SELECT COUNT(DISTINCT user_id)
FROM `logins`
WHERE login_timestamp BETWEEN '2015-09-29 14:05:07' AND '2015-09-29 14:05:13'

DISTINCT filtrará duplicatas e count retornará apenas a quantidade.

Combinado, isso se torna:
SELECT COUNT(DISTINCT user_id)
FROM `logins`
WHERE login_timestamp 
    BETWEEN DATE_FORMAT(ADDDATE(CURDATE(), INTERVAL 1- DAYOFWEEK(CURDATE()) DAY), "%Y-%m-%d 00:00:00") 
        AND DATE_FORMAT(ADDDATE(CURDATE(), INTERVAL 7- DAYOFWEEK(CURDATE()) DAY), "%Y-%m-%d 23:59:59")

Substitua CURDATE() com qualquer carimbo de data/hora para obter a contagem de login do usuário daquela semana.

Mas eu preciso dividir isso em dias, eu ouço você chorar. É claro! e é assim:

Primeiro, vamos traduzir nossos timestamps informativos apenas para os dados de data. Adicionamos DISTINCT porque não nos importamos que o mesmo usuário faça login duas vezes no mesmo dia. contamos usuários, não logins, certo? (note que voltamos aqui):
SELECT DISTINCT user_id, DATE_FORMAT(login_timestamp, "%Y-%m-%d")
FROM `logins`

Isso rende:
+---------+-----------------+
| user_id | login_timestamp |
+---------+-----------------+
|       1 | 2015-09-29      |
|       2 | 2015-09-29      |
|       4 | 2015-09-22      |
|   ...   |        ...      |
+---------+-----------------+

Esta consulta, vamos encerrar com um segundo, a fim de contar as aparições de cada data:
SELECT `login_timestamp`, count(*) AS 'count'
FROM (SELECT DISTINCT user_id, DATE_FORMAT(login_timestamp, "%Y-%m-%d") AS `login_timestamp` FROM `logins`) `loginsMod`
GROUP BY `login_timestamp`

Usamos count e um agrupamento para obter a lista por data, que retorna:
+-----------------+-------+
| login_timestamp | count |
+-----------------+-------+
| 2015-09-29      | 1     +
| 2015-09-22      | 2     +
+-----------------+-------+

E depois de todo o trabalho duro, ambos combinados:
SELECT `login_timestamp`, COUNT(*)
FROM (
SELECT DISTINCT user_id, DATE_FORMAT(login_timestamp, "%Y-%m-%d") AS `login_timestamp`
FROM `logins`
WHERE login_timestamp BETWEEN DATE_FORMAT(ADDDATE(CURDATE(), INTERVAL 1- DAYOFWEEK(CURDATE()) DAY), "%Y-%m-%d 00:00:00") AND DATE_FORMAT(ADDDATE(CURDATE(), INTERVAL 7- DAYOFWEEK(CURDATE()) DAY), "%Y-%m-%d 23:59:59")) `loginsMod`
GROUP BY `login_timestamp`;

Fornecerá um detalhamento diário de logins por dia nesta semana. Novamente, substitua CURDATE() para obter uma semana diferente.

Quanto aos próprios usuários que efetuaram login, vamos combinar as mesmas coisas em uma ordem diferente:
SELECT `user_id`
FROM (
    SELECT `user_id`, COUNT(*) AS `login_count`
    FROM (
        SELECT DISTINCT `user_id`, DATE_FORMAT(`login_timestamp`, "%Y-%m-%d")
        FROM `logins`) `logins`
    GROUP BY `user_id`) `logincounts`
WHERE `login_count` > 6

Eu tenho duas consultas internas, a primeira é logins :
SELECT DISTINCT `user_id`, DATE_FORMAT(`login_timestamp`, "%Y-%m-%d")
FROM `logins`

Fornecerá a lista de usuários e os dias em que efetuaram login, sem duplicatas.

Então temos logincounts :
SELECT `user_id`, COUNT(*) AS `login_count`
FROM `logins` -- See previous subquery.
GROUP BY `user_id`) `logincounts`

Retornará a mesma lista, com uma contagem de quantos logins cada usuário teve.

E por último:SELECIONE user_id DE logincounts -- Veja a subconsulta anterior.WHERE login_count> 6

Filtrando aqueles que não logaram 7 vezes e descartando a coluna de data.

Isso meio que ficou longo, mas acho que está cheio de ideias e acho que com certeza pode ajudar a responder de uma forma interessante em uma entrevista de trabalho. :)