sql >> Base de Datos >  >> RDS >> Mysql

Cuente los usuarios activos usando la marca de tiempo de inicio de sesión en MySQL

Voy a demostrar esa idea basándome en lo que tiene más sentido para mí y en la forma en que respondería si la pregunta se presentara de la misma manera que aquí:

Primero, supongamos un conjunto de datos como tal, llamaremos a la tabla 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 |
|   ...   |          ...        |
+---------+---------------------+

Puede haber otras columnas, pero no nos importan.

En primer lugar, debemos determinar los límites de esa semana, para eso podemos usar ADDDATE() . Combinado con la idea de que la fecha de hoy es el día de la semana de hoy (DAYOFWEEK() de MySQL ), es la fecha del domingo.

Por ejemplo:si hoy es miércoles 10, Wed - 3 = Sun , por lo tanto 10 - 3 = 7 , y podemos esperar que el domingo sea el 7.

Podemos obtener WeekStart y WeekEnd marcas de tiempo de esta manera:

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:en PostgreSQL hay un DATE_TRUNC() función que devuelve el comienzo de una unidad de tiempo específica, dada una fecha, como inicio de semana, mes, hora, etc. Pero eso no está disponible en MySQL.

A continuación, utilicemos WeekStart y WeekEnd para hacer clic en nuestro conjunto de datos, en este ejemplo solo mostraré cómo filtrar, usando fechas codificadas:

SELECT *
FROM `logins`
WHERE login_timestamp BETWEEN '2015-09-29 14:05:07' AND '2015-09-29 14:05:13'

Esto debería devolver nuestro conjunto de datos fragmentado, con solo resultados relevantes:

+---------+---------------------+
| user_id |   login_timestamp   |
+---------+---------------------+
|       2 | 2015-09-29 14:05:08 |
|       1 | 2015-09-29 14:05:12 |
+---------+---------------------+

Entonces podemos reducir nuestro conjunto de resultados a solo el user_id s, y filtre los duplicados. luego cuenta, de esta manera:

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á los duplicados y count devolverá solo la cantidad.

Combinado, esto se convierte en:

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")

Reemplazar CURDATE() con cualquier marca de tiempo para obtener el recuento de inicios de sesión de usuarios de esa semana.

Pero necesito dividir esto en días, te escucho llorar. ¡Por supuesto! y así es como:

Primero, traduzcamos nuestras marcas de tiempo sobreinformativas a solo los datos de fecha. Agregamos DISTINCT porque no nos importa que el mismo usuario inicie sesión dos veces el mismo día. contamos los usuarios, no los inicios de sesión, ¿verdad? (tenga en cuenta que damos un paso atrás aquí):

SELECT DISTINCT user_id, DATE_FORMAT(login_timestamp, "%Y-%m-%d")
FROM `logins`

Esto produce:

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

Esta consulta, la envolveremos con un segundo, para contar las apariciones de cada fecha:

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 y una agrupación para obtener la lista por fecha, que devuelve:

+-----------------+-------+
| login_timestamp | count |
+-----------------+-------+
| 2015-09-29      | 1     +
| 2015-09-22      | 2     +
+-----------------+-------+

Y después de todo el trabajo 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`;

Le dará un desglose diario de inicios de sesión por día en esta semana. Nuevamente, reemplace CURDATE() para tener una semana diferente.

En cuanto a los propios usuarios que iniciaron sesión, combinemos las mismas cosas en un orden 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

Tengo dos consultas internas, la primera es logins :

SELECT DISTINCT `user_id`, DATE_FORMAT(`login_timestamp`, "%Y-%m-%d")
FROM `logins`

Proporcionará la lista de usuarios y los días en que iniciaron sesión, sin duplicados.

Entonces tenemos logincounts :

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

Devolverá la misma lista, con un recuento de cuántos inicios de sesión tenía cada usuario.

Y por último:SELECCIONE user_id DESDE logincounts -- Ver subconsulta anterior.WHERE login_count> 6

Filtrando a aquellos que no iniciaron sesión 7 veces y eliminando la columna de fecha.

Esto se hizo un poco largo, pero creo que está lleno de ideas y creo que definitivamente puede ayudar a responder de una manera interesante en una entrevista de trabajo. :)