Bạn có thể thực hiện điều này bằng cách sử dụng liên kết tự bên ngoài được dịch chuyển kết hợp với một biến. Xem giải pháp này:
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
Điều này sẽ trả về 1
hoặc 0
dựa trên việc người dùng đã đăng nhập trong 30 ngày liên tục trở lên vào lúc ANYTIME trong quá khứ.
Gánh nặng của truy vấn này thực sự nằm ở lựa chọn con đầu tiên. Hãy xem xét kỹ hơn để chúng ta có thể hiểu rõ hơn về cách hoạt động của điều này:
Với tập dữ liệu mẫu sau:
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');
Truy vấn này:
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
Sẽ sản xuất:
Như bạn có thể thấy, những gì chúng tôi đang làm là chuyển dịch bảng đã tham gia trước +1 ngày. Đối với mỗi ngày không liên tiếp với ngày trước, một NULL
giá trị được tạo bởi LEFT JOIN.
Bây giờ chúng ta đã biết đâu là những ngày không liên tiếp, chúng tôi có thể sử dụng một biến để phân biệt từng tập hợp của những ngày liên tiếp bằng cách phát hiện xem các hàng của bảng đã chuyển có phải là NULL
hay không . Nếu chúng là NULL
, các ngày không liên tiếp, vì vậy chỉ cần tăng biến. Nếu chúng NOT NULL
, sau đó không tăng biến:
Sau khi chúng tôi đã phân biệt từng nhóm ngày liên tiếp với biến tăng dần, thì việc nhóm lại theo từng "nhóm" (như được định nghĩa trong consec_set
cột) và sử dụng HAVING
để lọc ra bất kỳ tập hợp nào có ít hơn số ngày liên tiếp được chỉ định (trong ví dụ của bạn là 30 ngày):
Sau đó, cuối cùng, chúng tôi quấn ĐÓ truy vấn và chỉ cần đếm số bộ có 30 ngày liên tiếp trở lên. Nếu có một hoặc nhiều bộ này, hãy trả về 1
, nếu không thì trả về 0
.