Mysql
 sql >> Cơ Sở Dữ Liệu >  >> RDS >> Mysql

Kiểm tra x ngày liên tiếp - dấu thời gian đã cho trong cơ sở dữ liệu

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 .

Xem Bản trình diễn từng bước của SQLFiddle



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Việc thêm 'LIMIT 1' vào các truy vấn MySQL có làm cho chúng nhanh hơn khi bạn biết rằng sẽ chỉ có 1 kết quả không?

  2. Cách hàm REGEXP_INSTR () hoạt động trong MySQL

  3. create_series () tương đương trong MySQL

  4. Chọn phần trăm TOP X (hoặc dưới cùng) cho các giá trị số trong MySQL

  5. Độ dài chỉ mục varchar của MySQL