Du kan opnå dette ved at bruge en forskudt selv-ydre-join i forbindelse med en variabel. Se denne løsning:
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
Dette returnerer enten en 1
eller en 0
baseret på, om en bruger har logget ind i 30 på hinanden følgende dage eller mere på ANYTIME i fortiden.
Hovedparten af denne forespørgsel er virkelig i det første undervalg. Lad os se nærmere, så vi bedre kan forstå, hvordan dette fungerer:
Med følgende eksempeldatasæt:
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');
Denne forespørgsel:
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
Vil producere:
Som du kan se, er det, vi gør, at skifte den samlede tabel med +1 dag. For hver dag, der ikke er fortløbende med den foregående dag, en NULL
værdi genereres af LEFT JOIN.
Nu hvor vi ved det hvor de ikke-på hinanden følgende dage er, kan vi bruge en variabel til at differentiere hvert sæt af på hinanden følgende dage ved at detektere, om den flyttede tabels rækker er NULL
. Hvis de er NULL
, dagene er ikke fortløbende, så du skal bare øge variablen. Hvis de er NOT NULL
, så skal du ikke øge variablen:
Efter at vi har differentieret hvert sæt af på hinanden følgende dage med den inkrementerende variabel, er det så bare et simpelt spørgsmål om at gruppere efter hvert "sæt" (som defineret i consec_set
kolonne) og ved hjælp af HAVING
for at bortfiltrere ethvert sæt, der har mindre end de angivne på hinanden følgende dage (30 i dit eksempel):
Så afslutter vi DET forespørg og tæl simpelthen antallet af sæt, der havde 30 eller flere på hinanden følgende dage. Hvis der var et eller flere af disse sæt, så returner 1
, ellers returner 0
.