Para obter uma contagem de "Usuário médio semanal" (pelo meu entendimento de sua especificação... "para cada dia, a contagem de user_ids distintos vistos durante esse dia e nos seis dias anteriores"), uma consulta nos moldes da abaixo poderia ser usado. (A consulta também retorna a contagem de "Usuário médio diário".
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
(Ainda não executei um teste disso; mas farei mais tarde e atualizarei esta declaração se forem necessárias correções.)
Esta consulta está se juntando à lista de usuários de um determinado dia (do
u
rowsource), para um conjunto de dias da tabela de log (o d
fonte de linha). Observe o literal "7" que aparece no predicado de junção (a cláusula ON), que é o que está fazendo a lista de usuários "combinar" com os 6 dias anteriores. Observe que isso também pode ser estendido para obter a contagem de usuários distintos nos últimos 3 dias, por exemplo, adicionando outra expressão na lista SELECT.
, COUNT(DISTINCT IF(u.day<=d.day AND u.day>d.day-3,u.user_id,NULL)) AS 3day
Esse literal "7" pode ser aumentado para obter um alcance maior. E esse literal 3 na expressão acima pode ser alterado para obter qualquer número de dias... só precisamos ter certeza de que temos linhas suficientes do dia anterior (de
d
) unido a cada linha de u
. NOTA DE DESEMPENHO:Devido às visualizações inline (ou tabelas derivadas, como o MySQL as chama), esta consulta pode não ser muito rápida, pois os conjuntos de resultados para essas visualizações inline devem ser materializados em tabelas MyISAM intermediárias.
A visualização inline com o alias
u
pode não ser o ideal; pode ser mais rápido juntar-se diretamente à tabela de log. Eu estava pensando em obter uma lista exclusiva de usuários para um determinado dia, que é o que essa consulta na visualização inline me trouxe. Era apenas mais fácil para mim conceituar o que estava acontecendo. E eu estava pensando que se você tivesse centenas do mesmo usuário inserido por dia, a visualização inline eliminaria um monte de duplicatas, antes de fazermos a junção com os outros dias. Uma cláusula WHERE para limitar o número de dias que estão retornando seria melhor adicionado dentro do u
e d
visualizações em linha. (O d
a visualização em linha precisaria incluir um extra de 6 dias anteriores.) Em outra nota, se a coluna ts for o tipo de dados TIMESTAMP, eu estaria mais inclinado a usar um
DATE(ts)
expressão para extrair a parte da data. Mas isso retornaria um tipo de dados DATE no conjunto de resultados, em vez de um inteiro, que seria diferente do conjunto de resultados especificado.) 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