U kunt dit bereiken met behulp van een verschoven self-outer-join in combinatie met een variabele. Zie deze oplossing:
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
Dit geeft een 1
. terug of een 0
op basis van of een gebruiker 30 opeenvolgende dagen of langer heeft ingelogd op ANYTIME in het verleden.
De dupe van deze query zit echt in de eerste subselect. Laten we eens nader kijken, zodat we beter kunnen begrijpen hoe dit werkt:
Met de volgende voorbeeldgegevensset:
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');
Deze vraag:
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
Zal produceren:
Zoals je kunt zien, is wat we aan het doen zijn verschuiven de samengevoegde tafel met +1 dag. Voor elke dag die niet opeenvolgend is met de voorgaande dag, een NULL
waarde wordt gegenereerd door de LEFT JOIN.
Nu we weten waar de niet-opeenvolgende dagen zijn, kunnen we een variabele gebruiken om elke set te onderscheiden van opeenvolgende dagen door te detecteren of de rijen van de verschoven tabel al dan niet NULL
zijn . Als ze NULL
zijn , de dagen zijn niet opeenvolgend, dus verhoog de variabele gewoon. Als ze NOT NULL
zijn , verhoog de variabele dan niet:
Nadat we elke reeks opeenvolgende dagen hebben onderscheiden met de oplopende variabele, is het een kwestie van groeperen op elke "set" (zoals gedefinieerd in de consec_set
kolom) en gebruik HAVING
om elke set uit te filteren die minder dan de opgegeven opeenvolgende dagen heeft (30 in uw voorbeeld):
Tot slot sluiten we DAT . af zoek en tel eenvoudig het aantal sets met 30 of meer opeenvolgende dagen. Als er een of meer van deze sets waren, retourneer dan 1
, retourneer anders 0
.