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. :)