Aby uzyskać „średnią liczbę użytkowników tygodniowo” (zgodnie z moim rozumieniem Twojej specyfikacji... „dla każdego dnia, liczba różnych identyfikatorów użytkownika widocznych w ciągu tego dnia i poprzednich sześciu dni”), zapytanie zgodne z poniższym może być używany. (Zapytanie zwraca również „dzienną średnią liczbę użytkowników”.
SELECT d.day
, COUNT(DISTINCT u.user_id) AS wau
, COUNT(DISTINCT IF(u.day=d.day,u.user_id,NULL)) AS dau
FROM ( SELECT FLOOR(k.ts/86400) AS `day`
FROM `log` k
GROUP BY `day`
) d
JOIN ( SELECT FLOOR(l.ts/86400) AS `day`
, l.user_id
FROM `log` l
GROUP BY `day`, l.user_id
) u
ON u.day <= d.day
AND u.day > d.day - 7
GROUP BY d.day
ORDER BY d.day
(Jeszcze nie przeprowadziłem testu tego; ale zrobię to później i zaktualizuję to oświadczenie, jeśli będą potrzebne jakiekolwiek poprawki.)
To zapytanie dołącza do listy użytkowników na dany dzień (z u
rowsource) do zestawu dni z tabeli dziennika (d
źródło wierszy). Zwróć uwagę na literał „7”, który pojawia się w predykacie złączenia (klauzula ON), to właśnie on powoduje „dopasowanie” listy użytkowników do ostatnich 6 dni.
Zwróć uwagę, że można to również rozszerzyć, aby uzyskać liczbę odrębnych użytkowników w ciągu ostatnich 3 dni, na przykład przez dodanie innego wyrażenia na liście SELECT.
, COUNT(DISTINCT IF(u.day<=d.day AND u.day>d.day-3,u.user_id,NULL)) AS 3day
Dosłowne „7” można zwiększyć, aby uzyskać większy zasięg. I ten literał 3 w powyższym wyrażeniu można zmienić, aby uzyskać dowolną liczbę dni... musimy tylko upewnić się, że mamy wystarczająco dużo wierszy z poprzedniego dnia (z d
) dołączony do każdego wiersza z u
.
INFORMACJA O WYDAJNOŚCI:Ze względu na widoki wbudowane (lub tabele pochodne, jak je nazywa MySQL), to zapytanie może nie być zbyt szybkie, ponieważ zestawy wyników dla tych widoków wbudowanych muszą zostać zmaterializowane w pośrednich tabelach MyISAM.
Widok wbudowany aliasem u
może nie być optymalna; może być szybciej dołączyć bezpośrednio do tabeli dziennika. Myślałem w kategoriach uzyskania unikalnej listy użytkowników na dany dzień, co dało mi to zapytanie w widoku wbudowanym. Po prostu łatwiej było mi pojąć, co się dzieje. Myślałem, że jeśli masz setki tych samych użytkowników wprowadzonych w ciągu dnia, widok wbudowany wyeliminowałby całą masę duplikatów, zanim zrobimy połączenie z innymi dniami. Klauzula WHERE ograniczająca liczbę dni, w których powracających najlepiej dodać w u
i d
widoki wbudowane. (d
widok wbudowany musiałby obejmować dodatkowe wcześniejsze 6 dni).
Z drugiej strony, jeśli kolumna ts jest typem danych TIMESTAMP, byłbym bardziej skłonny użyć DATE(ts)
wyrażenie do wyodrębnienia części daty. Ale to zwróci typ danych DATE w zestawie wyników zamiast liczby całkowitej, która byłaby inna niż określony zestaw wyników).
SELECT d.day
, COUNT(DISTINCT u.user_id) AS wau
, COUNT(DISTINCT IF(u.day=d.day,u.user_id,NULL)) AS dau
FROM ( SELECT DATE(k.ts) AS `day`
FROM `log` k
GROUP BY `day`
) d
JOIN ( SELECT DATE(l.ts) AS `day`
, l.user_id
FROM `log` l
GROUP BY `day`, l.user_id
) u
ON u.day <= d.day
AND u.day > DATE_ADD(d.day, INTERVAL -7 DAY)
GROUP BY d.day
ORDER BY d.day