Можете да постигнете това с помощта на изместено самостоятелно външно присъединяване във връзка с променлива. Вижте това решение:
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
Това ще върне или 1
или 0
въз основа на това дали потребителят е влизал за 30 последователни дни или повече във ВСЯКО ВРЕМЕ в миналото.
Тежестта на тази заявка наистина е в първия подселекция. Нека да разгледаме по-отблизо, за да разберем по-добре как работи това:
Със следния примерен набор от данни:
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');
Тази заявка:
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
Ще произведе:
Както можете да видите, това, което правим, е изместване обединената маса с +1 ден. За всеки ден, който не е последователен с предходния ден, NULL
стойността се генерира от LEFT JOIN.
Сега, когато знаем където са непоследователните дни, можем да използваме променлива, за да разграничим всеки набор от последователни дни, като се установи дали изместените редове на таблицата са NULL
. Ако са NULL
, дните не са последователни, така че просто увеличете променливата. Ако те са NOT NULL
, след това не увеличавайте променливата:
След като разграничим всеки набор от последователни дни с увеличаващата се променлива, това е просто въпрос на групиране по всеки "набор" (както е дефинирано в consec_set
колона) и с помощта на HAVING
за да филтрирате всеки набор, който има по-малко от посочените последователни дни (30 във вашия пример):
След това накрая, увиваме ТО заявка и просто пребройте броя на наборите, които са имали 30 или повече последователни дни. Ако е имало един или повече от тези набори, върнете 1
, в противен случай върнете 0
.