sql >> Database teknologi >  >> RDS >> Mysql

Tæl aktive brugere ved hjælp af login-tidsstempel i MySQL

Jeg vil demonstrere en sådan idé baseret på, hvad der giver mest mening for mig, og den måde jeg ville svare på, hvis spørgsmålet blev præsenteret på samme måde som her:

Lad os først antage et datasæt som sådan, vi vil navngive tabellen 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 | | ... | ... | +---------+---------------------+

Der kan være andre kolonner, men dem har vi ikke noget imod.

Først og fremmest bør vi bestemme grænserne for den uge, til det kan vi bruge ADDDATE() . Kombineret med ideen om, at dagens dato-dagens uge-dag (MySQL's DAYOFWEEK() ), er søndagens dato.

For eksempel:Hvis i dag er onsdag den 10., Wed - 3 = Sun , således 10 - 3 = 7 , og vi kan forvente søndag den 7.

Vi kan få WeekStart og WeekEnd tidsstempler på denne måde:

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;
 

Bemærk:i PostgreSQL er der en DATE_TRUNC() funktion, som returnerer begyndelsen af ​​en specificeret tidsenhed, givet en dato, såsom ugestart, måned, time og så videre. Men det er ikke tilgængeligt i MySQL.

Lad os derefter bruge WeekStart og weekEnd for at klikke på vores datasæt, i dette eksempel vil jeg bare vise, hvordan man filtrerer ved hjælp af hårdkodede datoer:

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

Dette skulle returnere vores datasæt i skiver, med kun relevante resultater:

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

Vi kan derefter reducere vores resultatsæt til kun user_id s, og filtrer dubletter fra. så tæl på denne måde:

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 vil filtrere dubletter fra, og count returnerer kun beløbet.

Sammenlagt bliver dette:

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

Erstat CURDATE() med et hvilket som helst tidsstempel for at få den pågældende uges brugerloginantal.

Men jeg er nødt til at nedbryde det til dage, jeg hører dig græde. Selvfølgelig! og sådan er det:

Lad os først oversætte vores overinformative tidsstempler til kun datodata. Vi tilføjer DISTINCT fordi vi ikke har noget imod, at den samme bruger logger ind to gange samme dag. vi tæller brugere, ikke logins, ikke? (bemærk, at vi træder tilbage her):

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

Dette giver:

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

Denne forespørgsel vil vi afslutte med et sekund, for at tælle optrædener af hver dato:

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`
 

Vi bruger optælling og en gruppering for at få listen efter dato, som returnerer:

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

Og efter alt det hårde arbejde, begge dele kombineret:

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`;
 

Giver dig en daglig oversigt over logins pr. dag i denne uge. Igen skal du erstatte CURDATE() for at få en anderledes uge.

Hvad angår brugerne selv, der loggede ind, lad os kombinere de samme ting i en anden rækkefølge:

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
 

Jeg har to indre forespørgsler, den første er logins :

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

Giver listen over brugere og de dage, hvor de loggede på, uden dubletter.

Så har vi logincounts :

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

Vil returnere den samme liste med en optælling af hvor mange logins hver bruger havde.

Og til sidst:VÆLG user_id FRA logincounts -- Se tidligere underforespørgsel. WHERE login_count> 6

Filtrering af dem, der ikke loggede ind 7 gange, og droppe datokolonnen.

Det her blev lidt langt, men jeg synes, det er fyldt med ideer, og jeg tror bestemt, det kan hjælpe med at svare på en interessant måde i en arbejdssamtale. :)



  1. Første gangs databasedesign:er jeg overingeniør?

  2. En fortælling om to klyngefaktorer

  3. Er Oracles SYS_GUID() UUID RFC 4122 kompatibel?

  4. orakeludtryk af forkert type under kontrol af variabel