Esta consulta é modificada da que escrevi aqui:Análise de coorte em SQL
Aqui está a consulta final:
SELECT
STR_TO_DATE(CONCAT(tb.cohort, ' Monday'), '%X-%V %W') as date,
size,
w1,
w2,
w3,
w4,
w5,
w6,
w7
FROM (
SELECT u.cohort,
IFNULL(SUM(s.Offset = 0), 0) w1,
IFNULL(SUM(s.Offset = 1), 0) w2,
IFNULL(SUM(s.Offset = 2), 0) w3,
IFNULL(SUM(s.Offset = 3), 0) w4,
IFNULL(SUM(s.Offset = 4), 0) w5,
IFNULL(SUM(s.Offset = 5), 0) w6,
IFNULL(SUM(s.Offset = 6), 0) w7
FROM (
SELECT
UserId,
DATE_FORMAT(AddedDate, "%Y-%u") AS cohort
FROM users
) as u
LEFT JOIN (
SELECT DISTINCT
payments.UserId,
FLOOR(DATEDIFF(payments.PaymentDate, users.AddedDate)/7) AS Offset
FROM payments
LEFT JOIN users ON (users.UserId = payments.UserId)
) as s ON s.UserId = u.UserId
GROUP BY u.cohort
) as tb
LEFT JOIN (
SELECT DATE_FORMAT(AddedDate, "%Y-%u") dt, COUNT(*) size FROM users GROUP BY dt
) size ON tb.cohort = size.dt
Então, o cerne disso é pegar os usuários e a data em que eles se inscreveram e formatar a data pelo número da semana do ano, já que estamos fazendo uma coorte semanal.
SELECT
UserId,
DATE_FORMAT(AddedDate, "%Y-%u") AS cohort
FROM users
Como queremos agrupar por coorte, temos que colocar isso em uma subconsulta na parte FROM da consulta.
Então queremos juntar as informações de pagamento dos usuários.
SELECT DISTINCT
payments.UserId,
FLOOR(DATEDIFF(payments.PaymentDate, users.AddedDate)/7) AS Offset
FROM payments
LEFT JOIN users ON (users.UserId = payments.UserId)
Isso obterá eventos de pagamento semanais exclusivos por usuário pelo número de semanas em que eles são usuários. Usamos distintos porque se um usuário fez duas compras em uma semana, não queremos contar como dois usuários.
Não usamos apenas a tabela de pagamentos, pois alguns usuários podem se cadastrar e não receber pagamentos. Então, selecionamos na tabela de usuários e juntamos na tabela de pagamentos.
Você então agrupa por semana - u.cohort. Em seguida, você agrega os números das semanas para descobrir quantas pessoas efetuaram pagamentos nas semanas após a inscrição.
A versão do mysql que usei tinha sql_mode definido como only_full_group_by. Então, para obter o tamanho da coorte, coloquei a maior parte da consulta na subconsulta para que eu pudesse juntar os usuários para obter o tamanho da coorte.
Considerações adicionais:
Filtrar por semanas é simples. tb.cohort> data inicial e tb.cohort
Você pode considerar o uso de uma tabela de calendário para cobrir os casos em que não há inscrições de usuários durante a semana.
Aqui está um violino com tudo funcionando:http://sqlfiddle.com/#!9/172dbe/ 1