Můžete toho dosáhnout pomocí posunutého vnějšího spojení ve spojení s proměnnou. Viz toto řešení:
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
To vrátí buď 1
nebo 0
podle toho, zda se uživatel přihlásil po dobu 30 po sobě jdoucích dnů nebo déle v KDYKOLI v minulosti.
Hlavní nápor tohoto dotazu je skutečně v prvním podvýběru. Podívejme se blíže, abychom lépe porozuměli tomu, jak to funguje:
S následujícím příkladem datové sady:
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');
Tento dotaz:
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
Vyrobí:
Jak můžete vidět, to, co děláme, je posouvání spojený stůl o +1 den. Pro každý den, který není po sobě jdoucí s předchozím dnem, NULL
hodnotu generuje LEFT JOIN.
Nyní, když víme tam, kde jsou dny, které po sobě nejdou, můžeme použít proměnnou k rozlišení každé množiny po sobě jdoucích dnů tím, že zjistí, zda jsou řádky posunuté tabulky NULL
. Pokud jsou NULL
, dny nejsou po sobě jdoucí, takže stačí zvýšit proměnnou. Pokud jsou NOT NULL
, pak nezvyšujte proměnnou:
Poté, co jsme odlišili každou sadu po sobě jdoucích dnů pomocí inkrementační proměnné, je to pak jen jednoduchá záležitost seskupení podle každé "množiny" (jak je definováno v consec_set
sloupec) a pomocí HAVING
pro odfiltrování jakékoli sady, která má méně než zadané po sobě jdoucí dny (30 ve vašem příkladu):
Pak nakonec zabalíme TO dotaz a jednoduše spočítat počet sad, které měly 30 nebo více po sobě jdoucích dnů. Pokud existuje jedna nebo více těchto sad, vraťte 1
, jinak vraťte 0
.