Puoi farlo usando un self-outer-join spostato insieme a una variabile. Vedi questa soluzione:
SELECT IF(COUNT(1) > 0, 1, 0) AS has_consec
FROM
(
SELECT *
FROM
(
SELECT IF(b.login_date IS NULL, @val:[email protected]+1, @val) AS consec_set
FROM tbl a
CROSS JOIN (SELECT @val:=0) var_init
LEFT JOIN tbl b ON
a.user_id = b.user_id AND
a.login_date = b.login_date + INTERVAL 1 DAY
WHERE a.user_id = 1
) a
GROUP BY a.consec_set
HAVING COUNT(1) >= 30
) a
Questo restituirà un 1
o un 0
in base al fatto che un utente abbia effettuato l'accesso per 30 giorni consecutivi o più a IN QUALSIASI MOMENTO nel passato.
Il peso di questa query è davvero nella prima sottoselezione. Diamo un'occhiata più da vicino in modo da poter capire meglio come funziona:
Con il seguente set di dati di esempio:
CREATE TABLE tbl (
user_id INT,
login_date DATE
);
INSERT INTO tbl VALUES
(1, '2012-04-01'), (2, '2012-04-02'),
(1, '2012-04-25'), (2, '2012-04-03'),
(1, '2012-05-03'), (2, '2012-04-04'),
(1, '2012-05-04'), (2, '2012-05-04'),
(1, '2012-05-05'), (2, '2012-05-06'),
(1, '2012-05-06'), (2, '2012-05-08'),
(1, '2012-05-07'), (2, '2012-05-09'),
(1, '2012-05-09'), (2, '2012-05-11'),
(1, '2012-05-10'), (2, '2012-05-17'),
(1, '2012-05-11'), (2, '2012-05-18'),
(1, '2012-05-12'), (2, '2012-05-19'),
(1, '2012-05-16'), (2, '2012-05-20'),
(1, '2012-05-19'), (2, '2012-05-21'),
(1, '2012-05-20'), (2, '2012-05-22'),
(1, '2012-05-21'), (2, '2012-05-25'),
(1, '2012-05-22'), (2, '2012-05-26'),
(1, '2012-05-25'), (2, '2012-05-27'),
(2, '2012-05-28'),
(2, '2012-05-29'),
(2, '2012-05-30'),
(2, '2012-05-31'),
(2, '2012-06-01'),
(2, '2012-06-02');
Questa domanda:
SELECT a.*, b.*, IF(b.login_date IS NULL, @val:[email protected]+1, @val) AS consec_set
FROM tbl a
CROSS JOIN (SELECT @val:=0) var_init
LEFT JOIN tbl b ON
a.user_id = b.user_id AND
a.login_date = b.login_date + INTERVAL 1 DAY
WHERE a.user_id = 1
Produrrà:
Come puoi vedere, quello che stiamo facendo è cambiare il tavolo unito di +1 giorno. Per ogni giorno non consecutivo al giorno precedente, un NULL
il valore è generato dal LEFT JOIN.
Ora che sappiamo dove sono i giorni non consecutivi, possiamo usare una variabile per differenziare ogni insieme di giorni consecutivi rilevando se le righe della tabella spostata sono NULL
. Se sono NULL
, i giorni non sono consecutivi, quindi basta aumentare la variabile. Se sono NOT NULL
, quindi non incrementare la variabile:
Dopo aver differenziato ogni serie di giorni consecutivi con la variabile incrementale, è solo questione di raggruppare per ogni "serie" (come definito in consec_set
colonna) e utilizzando HAVING
per filtrare qualsiasi set che ha meno di giorni consecutivi specificati (30 nel tuo esempio):
Quindi, infine, avvolgiamo QUESTO interrogare e contare semplicemente il numero di set che hanno avuto 30 o più giorni consecutivi. Se c'erano uno o più di questi set, restituisci 1
, altrimenti restituisci 0
.