Jag kommer att demonstrera en sådan idé baserat på vad som är mest meningsfullt för mig och hur jag skulle svara om frågan presenterades på samma sätt som här:
Låt oss först anta en datamängd som sådan, vi kommer att namnge 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 |
| ... | ... |
+---------+---------------------+
Det kan finnas andra kolumner, men de har vi inget emot.
Först och främst bör vi bestämma gränserna för den veckan, för det kan vi använda ADDDATE()
. Kombinerat med tanken att dagens datum-dagens veckodag (MySQL:s DAYOFWEEK()
), är söndagens datum.
Till exempel:Om idag är onsdagen den 10:e, Wed - 3 = Sun
, alltså 10 - 3 = 7
, och vi kan förvänta oss att söndagen blir den 7.
Vi kan få WeekStart
och WeekEnd
tidsstämplar på detta sätt:
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;
Obs:i PostgreSQL finns en DATE_TRUNC()
funktion som returnerar början av en angiven tidsenhet, givet ett datum, som veckostart, månad, timme och så vidare. Men det är inte tillgängligt i MySQL.
Låt oss sedan använda WeekStart och weekEnd för att klicka på vår datamängd, i det här exemplet visar jag bara hur man filtrerar med hårdkodade datum:
SELECT *
FROM `logins`
WHERE login_timestamp BETWEEN '2015-09-29 14:05:07' AND '2015-09-29 14:05:13'
Detta bör returnera vår datamängd i skivor, med endast relevanta resultat:
+---------+---------------------+
| user_id | login_timestamp |
+---------+---------------------+
| 2 | 2015-09-29 14:05:08 |
| 1 | 2015-09-29 14:05:12 |
+---------+---------------------+
Vi kan sedan minska vår resultatuppsättning till endast user_id
s och filtrera bort dubbletter. räkna sedan på det här sättet:
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
kommer att filtrera bort dubbletter, och count returnerar bara beloppet.
Tillsammans blir detta:
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")
Ersätt CURDATE()
med valfri tidsstämpel för att få veckans antal användarinloggningar.
Men jag måste bryta ner det här till dagar, jag hör dig gråta. Självklart! och så här:
Låt oss först översätta våra överinformativa tidsstämplar till bara datumdata. Vi lägger till DISTINCT
eftersom vi inte har något emot att samma användare loggar in två gånger samma dag. vi räknar användare, inte inloggningar, eller hur? (observera att vi går tillbaka här):
SELECT DISTINCT user_id, DATE_FORMAT(login_timestamp, "%Y-%m-%d")
FROM `logins`
Detta ger:
+---------+-----------------+
| user_id | login_timestamp |
+---------+-----------------+
| 1 | 2015-09-29 |
| 2 | 2015-09-29 |
| 4 | 2015-09-22 |
| ... | ... |
+---------+-----------------+
Den här frågan kommer vi att avsluta med en sekund för att räkna framträdanden för varje datum:
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 använder räkning och en gruppering för att få listan efter datum, vilket returnerar:
+-----------------+-------+
| login_timestamp | count |
+-----------------+-------+
| 2015-09-29 | 1 +
| 2015-09-22 | 2 +
+-----------------+-------+
Och efter allt hårt arbete, båda tillsammans:
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`;
Ger dig en daglig uppdelning av inloggningar per dag den här veckan. Återigen, ersätt CURDATE()
för att få en annan vecka.
När det gäller användarna själva som loggat in, låt oss kombinera samma saker i en annan ordning:
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
Jag har två inre frågor, den första är logins
:
SELECT DISTINCT `user_id`, DATE_FORMAT(`login_timestamp`, "%Y-%m-%d")
FROM `logins`
Kommer att tillhandahålla listan över användare och dagar då de loggat in utan dubbletter.
Sedan har vi logincounts
:
SELECT `user_id`, COUNT(*) AS `login_count`
FROM `logins` -- See previous subquery.
GROUP BY `user_id`) `logincounts`
Kommer att returnera samma lista, med en räkning av hur många inloggningar varje användare hade.
Och sist:VÄLJ user_id
FRÅN logincounts
-- Se föregående underfråga. WHERE login_count
> 6
Filtrera de som inte loggat in 7 gånger och släpper datumkolumnen.
Det här blev lite långt, men jag tror att det är fullt av idéer och jag tror att det definitivt kan hjälpa att svara på ett intressant sätt i en arbetsintervju. :)